WidgetRattus 0.2 → 0.3
raw patch · 50 files changed
+4878/−4215 lines, 50 filesdep +monomerdep ~ghcdep ~ghc-bootdep ~template-haskellbinary-addedPVP ok
version bump matches the API change (PVP)
Dependencies added: monomer
Dependency ranges changed: ghc, ghc-boot, template-haskell, text
API changes (from Hackage documentation)
- AsyncRattus: AllowLazyData :: AsyncRattus
- AsyncRattus: AllowRecursion :: AsyncRattus
- AsyncRattus: continuous :: Name -> Q [Dec]
- AsyncRattus: data AsyncRattus
- AsyncRattus: mapO :: Box (a -> b) -> O a -> O b
- AsyncRattus.Channels: C :: IO a -> C a
- AsyncRattus.Channels: [unC] :: C a -> IO a
- AsyncRattus.Channels: chan :: C (Chan a)
- AsyncRattus.Channels: class Producer p a | p -> a
- AsyncRattus.Channels: data Chan a
- AsyncRattus.Channels: delayC :: O (C a) -> C (O a)
- AsyncRattus.Channels: getCurrent :: Producer p a => p -> Maybe' a
- AsyncRattus.Channels: getInput :: IO (Box (O a) :* (a -> IO ()))
- AsyncRattus.Channels: getNext :: Producer p a => p -> (forall q. Producer q a => O q -> b) -> b
- AsyncRattus.Channels: instance AsyncRattus.Channels.Producer p a => AsyncRattus.Channels.Producer (AsyncRattus.InternalPrimitives.Box p) a
- AsyncRattus.Channels: instance AsyncRattus.Channels.Producer p a => AsyncRattus.Channels.Producer (AsyncRattus.InternalPrimitives.O p) a
- AsyncRattus.Channels: instance GHC.Base.Applicative AsyncRattus.Channels.C
- AsyncRattus.Channels: instance GHC.Base.Functor AsyncRattus.Channels.C
- AsyncRattus.Channels: instance GHC.Base.Monad AsyncRattus.Channels.C
- AsyncRattus.Channels: mkInput :: Producer p a => p -> IO (Box (O a))
- AsyncRattus.Channels: newtype C a
- AsyncRattus.Channels: setOutput :: Producer p a => p -> (a -> IO ()) -> IO ()
- AsyncRattus.Channels: startEventLoop :: IO ()
- AsyncRattus.Channels: timer :: Int -> Box (O ())
- AsyncRattus.Channels: wait :: Chan a -> O a
- AsyncRattus.Future: (:>:) :: !a -> !O (F (SigF a)) -> SigF a
- AsyncRattus.Future: Now :: !a -> F a
- AsyncRattus.Future: Wait :: !O (F a) -> F a
- AsyncRattus.Future: bindF :: F a -> Box (a -> F b) -> F b
- AsyncRattus.Future: current :: SigF a -> a
- AsyncRattus.Future: data F a
- AsyncRattus.Future: data SigF a
- AsyncRattus.Future: filter :: Box (a -> Bool) -> SigF a -> F (SigF a)
- AsyncRattus.Future: filterAwait :: Box (a -> Bool) -> F (SigF a) -> F (SigF a)
- AsyncRattus.Future: filterMap :: Box (a -> Maybe' b) -> SigF a -> F (SigF b)
- AsyncRattus.Future: filterMapAwait :: Box (a -> Maybe' b) -> F (SigF a) -> F (SigF b)
- AsyncRattus.Future: fromSig :: Sig a -> SigF a
- AsyncRattus.Future: future :: SigF a -> O (F (SigF a))
- AsyncRattus.Future: instance AsyncRattus.Channels.Producer (AsyncRattus.Future.OneShot a) a
- AsyncRattus.Future: instance AsyncRattus.Channels.Producer (AsyncRattus.Future.SigF a) a
- AsyncRattus.Future: instance AsyncRattus.Channels.Producer p a => AsyncRattus.Channels.Producer (AsyncRattus.Future.F p) a
- AsyncRattus.Future: map :: Box (a -> b) -> SigF a -> SigF b
- AsyncRattus.Future: mapAwait :: Box (a -> b) -> F (SigF a) -> F (SigF b)
- AsyncRattus.Future: mapF :: Box (a -> b) -> F a -> F b
- AsyncRattus.Future: mkSigF :: Box (O a) -> F (SigF a)
- AsyncRattus.Future: mkSigF' :: Box (O a) -> O (F (SigF a))
- AsyncRattus.Future: scan :: Stable b => Box (b -> a -> b) -> b -> SigF a -> SigF b
- AsyncRattus.Future: scanAwait :: Stable b => Box (b -> a -> b) -> b -> F (SigF a) -> F (SigF b)
- AsyncRattus.Future: switch :: SigF a -> F (SigF a) -> SigF a
- AsyncRattus.Future: switchAwait :: F (SigF a) -> F (SigF a) -> F (SigF a)
- AsyncRattus.Future: switchS :: Stable a => SigF a -> F (a -> SigF a) -> SigF a
- AsyncRattus.Future: sync :: O (F a) -> O (F b) -> O (F a :* F b)
- AsyncRattus.Future: syncF :: (Stable a, Stable b) => F a -> F b -> F (a :* b)
- AsyncRattus.Future: trigger :: Stable b => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c
- AsyncRattus.Future: triggerAwait :: Stable b => Box (a -> b -> c) -> F (SigF a) -> SigF b -> F (SigF c)
- AsyncRattus.Future: zipWith :: (Stable a, Stable b) => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c
- AsyncRattus.Future: zipWithAwait :: (Stable a, Stable b) => Box (a -> b -> c) -> a -> b -> F (SigF a) -> F (SigF b) -> F (SigF c)
- AsyncRattus.InternalPrimitives: Both :: !a -> !b -> Select a b
- AsyncRattus.InternalPrimitives: Box :: a -> Box a
- AsyncRattus.InternalPrimitives: Chan :: InputChannelIdentifier -> Chan a
- AsyncRattus.InternalPrimitives: Delay :: !Clock -> (InputValue -> a) -> O a
- AsyncRattus.InternalPrimitives: Fst :: !a -> !O b -> Select a b
- AsyncRattus.InternalPrimitives: Snd :: !O a -> !b -> Select a b
- AsyncRattus.InternalPrimitives: [ContinuousData] :: Continuous a => !Weak (IORef a) -> ContinuousData
- AsyncRattus.InternalPrimitives: [MoreInputs] :: !InputChannelIdentifier -> !a -> !InputValue -> InputValue
- AsyncRattus.InternalPrimitives: [OneInput] :: !InputChannelIdentifier -> !a -> InputValue
- AsyncRattus.InternalPrimitives: adv :: O a -> a
- AsyncRattus.InternalPrimitives: adv' :: O a -> InputValue -> a
- AsyncRattus.InternalPrimitives: asyncRattusError :: [Char] -> a
- AsyncRattus.InternalPrimitives: box :: a -> Box a
- AsyncRattus.InternalPrimitives: channelMember :: InputChannelIdentifier -> Clock -> Bool
- AsyncRattus.InternalPrimitives: class Continuous p
- AsyncRattus.InternalPrimitives: class Stable a
- AsyncRattus.InternalPrimitives: clockUnion :: Clock -> Clock -> Clock
- AsyncRattus.InternalPrimitives: data Box a
- AsyncRattus.InternalPrimitives: data ContinuousData
- AsyncRattus.InternalPrimitives: data InputValue
- AsyncRattus.InternalPrimitives: data O a
- AsyncRattus.InternalPrimitives: data Select a b
- AsyncRattus.InternalPrimitives: defaultPromote :: Continuous a => a -> Box a
- AsyncRattus.InternalPrimitives: delay :: a -> O a
- AsyncRattus.InternalPrimitives: emptyClock :: Clock
- AsyncRattus.InternalPrimitives: extractClock :: O a -> Clock
- AsyncRattus.InternalPrimitives: inputInClock :: InputValue -> Clock -> Bool
- AsyncRattus.InternalPrimitives: instance AsyncRattus.InternalPrimitives.Stable a => AsyncRattus.InternalPrimitives.Continuous a
- AsyncRattus.InternalPrimitives: never :: O a
- AsyncRattus.InternalPrimitives: newtype Chan a
- AsyncRattus.InternalPrimitives: nextProgress :: Continuous p => p -> Clock
- AsyncRattus.InternalPrimitives: progressAndNext :: Continuous p => InputValue -> p -> (p, Clock)
- AsyncRattus.InternalPrimitives: progressInternal :: Continuous p => InputValue -> p -> p
- AsyncRattus.InternalPrimitives: progressPromoteStore :: InputValue -> IO ()
- AsyncRattus.InternalPrimitives: progressPromoteStoreAtomic :: InputValue -> IO ()
- AsyncRattus.InternalPrimitives: progressPromoteStoreMutex :: MVar ()
- AsyncRattus.InternalPrimitives: promote :: Continuous a => a -> Box a
- AsyncRattus.InternalPrimitives: promoteInternal :: Continuous p => p -> Box p
- AsyncRattus.InternalPrimitives: promoteStore :: IORef [ContinuousData]
- AsyncRattus.InternalPrimitives: select :: O a -> O b -> Select a b
- AsyncRattus.InternalPrimitives: select' :: O a -> O b -> InputValue -> Select a b
- AsyncRattus.InternalPrimitives: singletonClock :: InputChannelIdentifier -> Clock
- AsyncRattus.InternalPrimitives: type Clock = IntSet
- AsyncRattus.InternalPrimitives: type InputChannelIdentifier = Int
- AsyncRattus.InternalPrimitives: unbox :: Box a -> a
- AsyncRattus.Plugin: AllowLazyData :: AsyncRattus
- AsyncRattus.Plugin: AllowRecursion :: AsyncRattus
- AsyncRattus.Plugin: data AsyncRattus
- AsyncRattus.Plugin: plugin :: Plugin
- AsyncRattus.Plugin.Annotation: AllowLazyData :: AsyncRattus
- AsyncRattus.Plugin.Annotation: AllowRecursion :: AsyncRattus
- AsyncRattus.Plugin.Annotation: ExpectError :: InternalAnn
- AsyncRattus.Plugin.Annotation: ExpectWarning :: InternalAnn
- AsyncRattus.Plugin.Annotation: data AsyncRattus
- AsyncRattus.Plugin.Annotation: data InternalAnn
- AsyncRattus.Plugin.Annotation: instance Data.Data.Data AsyncRattus.Plugin.Annotation.AsyncRattus
- AsyncRattus.Plugin.Annotation: instance Data.Data.Data AsyncRattus.Plugin.Annotation.InternalAnn
- AsyncRattus.Plugin.Annotation: instance GHC.Classes.Eq AsyncRattus.Plugin.Annotation.AsyncRattus
- AsyncRattus.Plugin.Annotation: instance GHC.Classes.Eq AsyncRattus.Plugin.Annotation.InternalAnn
- AsyncRattus.Plugin.Annotation: instance GHC.Classes.Ord AsyncRattus.Plugin.Annotation.AsyncRattus
- AsyncRattus.Plugin.Annotation: instance GHC.Classes.Ord AsyncRattus.Plugin.Annotation.InternalAnn
- AsyncRattus.Plugin.Annotation: instance GHC.Show.Show AsyncRattus.Plugin.Annotation.AsyncRattus
- AsyncRattus.Plugin.Annotation: instance GHC.Show.Show AsyncRattus.Plugin.Annotation.InternalAnn
- AsyncRattus.Primitives: Both :: !a -> !b -> Select a b
- AsyncRattus.Primitives: Fst :: !a -> !O b -> Select a b
- AsyncRattus.Primitives: Snd :: !O a -> !b -> Select a b
- AsyncRattus.Primitives: adv :: O a -> a
- AsyncRattus.Primitives: box :: a -> Box a
- AsyncRattus.Primitives: class Continuous p
- AsyncRattus.Primitives: class Stable a
- AsyncRattus.Primitives: data Box a
- AsyncRattus.Primitives: data O a
- AsyncRattus.Primitives: data Select a b
- AsyncRattus.Primitives: delay :: a -> O a
- AsyncRattus.Primitives: never :: O a
- AsyncRattus.Primitives: promote :: Continuous a => a -> Box a
- AsyncRattus.Primitives: select :: O a -> O b -> Select a b
- AsyncRattus.Primitives: unbox :: Box a -> a
- AsyncRattus.Signal: (:::) :: !a -> !O (Sig a) -> Sig a
- AsyncRattus.Signal: cond :: Stable a => Sig Bool -> Sig a -> Sig a -> Sig a
- AsyncRattus.Signal: const :: a -> Sig a
- AsyncRattus.Signal: current :: Sig a -> a
- AsyncRattus.Signal: data Sig a
- AsyncRattus.Signal: derivative :: forall a v. (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a) => Sig v -> Sig v
- AsyncRattus.Signal: filter :: Box (a -> Bool) -> Sig a -> IO (Box (O (Sig a)))
- AsyncRattus.Signal: filterAwait :: Box (a -> Bool) -> O (Sig a) -> IO (Box (O (Sig a)))
- AsyncRattus.Signal: filterMap :: Box (a -> Maybe' b) -> Sig a -> IO (Box (O (Sig b)))
- AsyncRattus.Signal: filterMapAwait :: Box (a -> Maybe' b) -> O (Sig a) -> IO (Box (O (Sig b)))
- AsyncRattus.Signal: future :: Sig a -> O (Sig a)
- AsyncRattus.Signal: getInputSig :: IO (Box (O (Sig a)) :* (a -> IO ()))
- AsyncRattus.Signal: infixr 5 :::
- AsyncRattus.Signal: instance AsyncRattus.Channels.Producer (AsyncRattus.Signal.Sig a) a
- AsyncRattus.Signal: instance AsyncRattus.Channels.Producer (AsyncRattus.Signal.SigMaybe a) a
- AsyncRattus.Signal: instance AsyncRattus.InternalPrimitives.Continuous a => AsyncRattus.InternalPrimitives.Continuous (AsyncRattus.Signal.Sig a)
- AsyncRattus.Signal: integral :: forall a v. (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a) => v -> Sig v -> Sig v
- AsyncRattus.Signal: interleave :: Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)
- AsyncRattus.Signal: map :: Box (a -> b) -> Sig a -> Sig b
- AsyncRattus.Signal: mapAwait :: Box (a -> b) -> O (Sig a) -> O (Sig b)
- AsyncRattus.Signal: mkBoxSig :: Box (O a) -> Box (O (Sig a))
- AsyncRattus.Signal: mkInputSig :: Producer p a => p -> IO (Box (O (Sig a)))
- AsyncRattus.Signal: mkSig :: Box (O a) -> O (Sig a)
- AsyncRattus.Signal: scan :: Continuous b => Box (b -> a -> b) -> b -> Sig a -> Sig b
- AsyncRattus.Signal: scanAwait :: Continuous b => Box (b -> a -> b) -> b -> O (Sig a) -> Sig b
- AsyncRattus.Signal: scanAwaitC :: Continuous b => Box (b -> a -> C b) -> b -> O (Sig a) -> C (Sig b)
- AsyncRattus.Signal: scanC :: Continuous b => Box (b -> a -> C b) -> b -> Sig a -> C (Sig b)
- AsyncRattus.Signal: scanMap :: Continuous b => Box (b -> a -> b) -> Box (b -> c) -> b -> Sig a -> Sig c
- AsyncRattus.Signal: switch :: Sig a -> O (Sig a) -> Sig a
- AsyncRattus.Signal: switchAwait :: O (Sig a) -> O (Sig a) -> O (Sig a)
- AsyncRattus.Signal: switchS :: Continuous a => Sig a -> O (a -> Sig a) -> Sig a
- AsyncRattus.Signal: trigger :: (Stable a, Stable b) => Box (a -> b -> c) -> Sig a -> Sig b -> IO (Box (Sig c))
- AsyncRattus.Signal: triggerAwait :: Stable b => Box (a -> b -> c) -> O (Sig a) -> Sig b -> IO (Box (O (Sig c)))
- AsyncRattus.Signal: update :: Continuous a => Sig a -> O (Sig (a -> a)) -> Sig a
- AsyncRattus.Signal: zip :: (Stable a, Stable b) => Sig a -> Sig b -> Sig (a :* b)
- AsyncRattus.Signal: zipWith :: (Stable a, Stable b) => Box (a -> b -> c) -> Sig a -> Sig b -> Sig c
- AsyncRattus.Signal: zipWith3 :: forall a b c d. (Stable a, Stable b, Stable c) => Box (a -> b -> c -> d) -> Sig a -> Sig b -> Sig c -> Sig d
- AsyncRattus.Strict: (+++) :: List a -> List a -> List a
- AsyncRattus.Strict: (:!) :: !a -> !List a -> List a
- AsyncRattus.Strict: (:*) :: !a -> !b -> (:*) a b
- AsyncRattus.Strict: -- structure <tt>l</tt>.
- AsyncRattus.Strict: -- | The <a>Item</a> type function returns the type of items of the
- AsyncRattus.Strict: Just' :: !a -> Maybe' a
- AsyncRattus.Strict: Nil :: List a
- AsyncRattus.Strict: Nothing' :: Maybe' a
- AsyncRattus.Strict: class () => IsList l where {
- AsyncRattus.Strict: curry' :: ((a :* b) -> c) -> a -> b -> c
- AsyncRattus.Strict: data List a
- AsyncRattus.Strict: data Maybe' a
- AsyncRattus.Strict: data a :* b
- AsyncRattus.Strict: delete' :: Eq a => a -> List a -> List a
- AsyncRattus.Strict: deleteBy' :: (a -> a -> Bool) -> a -> List a -> List a
- AsyncRattus.Strict: filter' :: (a -> Bool) -> List a -> List a
- AsyncRattus.Strict: fromList :: IsList l => [Item l] -> l
- AsyncRattus.Strict: fromListN :: IsList l => Int -> [Item l] -> l
- AsyncRattus.Strict: fromMaybe' :: a -> Maybe' a -> a
- AsyncRattus.Strict: fst' :: (a :* b) -> a
- AsyncRattus.Strict: infixr 2 :*
- AsyncRattus.Strict: infixr 8 :!
- AsyncRattus.Strict: init' :: List a -> List a
- AsyncRattus.Strict: instance (AsyncRattus.InternalPrimitives.Continuous a, AsyncRattus.InternalPrimitives.Continuous b) => AsyncRattus.InternalPrimitives.Continuous (a AsyncRattus.Strict.:* b)
- AsyncRattus.Strict: instance (Data.VectorSpace.VectorSpace v a, Data.VectorSpace.VectorSpace w a, GHC.Float.Floating a, GHC.Classes.Eq a) => Data.VectorSpace.VectorSpace (v AsyncRattus.Strict.:* w) a
- AsyncRattus.Strict: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (a AsyncRattus.Strict.:* b)
- AsyncRattus.Strict: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (a AsyncRattus.Strict.:* b)
- AsyncRattus.Strict: instance AsyncRattus.InternalPrimitives.Continuous a => AsyncRattus.InternalPrimitives.Continuous (AsyncRattus.Strict.List a)
- AsyncRattus.Strict: instance AsyncRattus.InternalPrimitives.Continuous a => AsyncRattus.InternalPrimitives.Continuous (AsyncRattus.Strict.Maybe' a)
- AsyncRattus.Strict: instance Data.Foldable.Foldable AsyncRattus.Strict.List
- AsyncRattus.Strict: instance Data.Traversable.Traversable AsyncRattus.Strict.List
- AsyncRattus.Strict: instance GHC.Base.Functor ((AsyncRattus.Strict.:*) a)
- AsyncRattus.Strict: instance GHC.Base.Functor AsyncRattus.Strict.List
- AsyncRattus.Strict: instance GHC.Classes.Eq a => GHC.Classes.Eq (AsyncRattus.Strict.List a)
- AsyncRattus.Strict: instance GHC.Classes.Eq a => GHC.Classes.Eq (AsyncRattus.Strict.Maybe' a)
- AsyncRattus.Strict: instance GHC.IsList.IsList (AsyncRattus.Strict.List a)
- AsyncRattus.Strict: instance GHC.Show.Show a => GHC.Show.Show (AsyncRattus.Strict.List a)
- AsyncRattus.Strict: instance GHC.Show.Show a => GHC.Show.Show (AsyncRattus.Strict.Maybe' a)
- AsyncRattus.Strict: listToMaybe' :: List a -> Maybe' a
- AsyncRattus.Strict: map' :: (a -> b) -> List a -> List b
- AsyncRattus.Strict: mapMaybe' :: (a -> Maybe' b) -> List a -> List b
- AsyncRattus.Strict: maybe' :: b -> (a -> b) -> Maybe' a -> b
- AsyncRattus.Strict: nub' :: Eq a => List a -> List a
- AsyncRattus.Strict: nubBy' :: (a -> a -> Bool) -> List a -> List a
- AsyncRattus.Strict: reverse' :: List a -> List a
- AsyncRattus.Strict: singleton :: a -> List a
- AsyncRattus.Strict: snd' :: (a :* b) -> b
- AsyncRattus.Strict: toList :: IsList l => l -> [Item l]
- AsyncRattus.Strict: type family Item l;
- AsyncRattus.Strict: uncurry' :: (a -> b -> c) -> (a :* b) -> c
- AsyncRattus.Strict: union' :: Eq a => List a -> List a -> List a
- AsyncRattus.Strict: unionBy' :: (a -> a -> Bool) -> List a -> List a -> List a
- AsyncRattus.Strict: zip' :: List a -> List b -> List (a :* b)
- AsyncRattus.Strict: zipWith' :: (a -> b -> c) -> List a -> List b -> List c
- AsyncRattus.Strict: }
+ WidgetRattus: AllowLazyData :: WidgetRattus
+ WidgetRattus: AllowRecursion :: WidgetRattus
+ WidgetRattus: continuous :: Name -> Q [Dec]
+ WidgetRattus: data WidgetRattus
+ WidgetRattus: mapO :: Box (a -> b) -> O a -> O b
+ WidgetRattus.Channels: C :: IO a -> C a
+ WidgetRattus.Channels: [unC] :: C a -> IO a
+ WidgetRattus.Channels: chan :: C (Chan a)
+ WidgetRattus.Channels: class Producer p a | p -> a
+ WidgetRattus.Channels: data Chan a
+ WidgetRattus.Channels: delayC :: O (C a) -> C (O a)
+ WidgetRattus.Channels: getCurrent :: Producer p a => p -> Maybe' a
+ WidgetRattus.Channels: getNext :: Producer p a => p -> (forall q. Producer q a => O q -> b) -> b
+ WidgetRattus.Channels: instance GHC.Base.Applicative WidgetRattus.Channels.C
+ WidgetRattus.Channels: instance GHC.Base.Functor WidgetRattus.Channels.C
+ WidgetRattus.Channels: instance GHC.Base.Monad WidgetRattus.Channels.C
+ WidgetRattus.Channels: instance WidgetRattus.Channels.Producer p a => WidgetRattus.Channels.Producer (WidgetRattus.InternalPrimitives.Box p) a
+ WidgetRattus.Channels: instance WidgetRattus.Channels.Producer p a => WidgetRattus.Channels.Producer (WidgetRattus.InternalPrimitives.O p) a
+ WidgetRattus.Channels: newtype C a
+ WidgetRattus.Channels: timer :: Int -> Box (O ())
+ WidgetRattus.Channels: wait :: Chan a -> O a
+ WidgetRattus.Future: (:>:) :: !a -> !O (F (SigF a)) -> SigF a
+ WidgetRattus.Future: Now :: !a -> F a
+ WidgetRattus.Future: Wait :: !O (F a) -> F a
+ WidgetRattus.Future: bindF :: F a -> Box (a -> F b) -> F b
+ WidgetRattus.Future: current :: SigF a -> a
+ WidgetRattus.Future: data F a
+ WidgetRattus.Future: data SigF a
+ WidgetRattus.Future: filter :: Box (a -> Bool) -> SigF a -> F (SigF a)
+ WidgetRattus.Future: filterAwait :: Box (a -> Bool) -> F (SigF a) -> F (SigF a)
+ WidgetRattus.Future: filterMap :: Box (a -> Maybe' b) -> SigF a -> F (SigF b)
+ WidgetRattus.Future: filterMapAwait :: Box (a -> Maybe' b) -> F (SigF a) -> F (SigF b)
+ WidgetRattus.Future: fromSig :: Sig a -> SigF a
+ WidgetRattus.Future: future :: SigF a -> O (F (SigF a))
+ WidgetRattus.Future: instance WidgetRattus.Channels.Producer (WidgetRattus.Future.OneShot a) a
+ WidgetRattus.Future: instance WidgetRattus.Channels.Producer (WidgetRattus.Future.SigF a) a
+ WidgetRattus.Future: instance WidgetRattus.Channels.Producer p a => WidgetRattus.Channels.Producer (WidgetRattus.Future.F p) a
+ WidgetRattus.Future: map :: Box (a -> b) -> SigF a -> SigF b
+ WidgetRattus.Future: mapAwait :: Box (a -> b) -> F (SigF a) -> F (SigF b)
+ WidgetRattus.Future: mapF :: Box (a -> b) -> F a -> F b
+ WidgetRattus.Future: mkSigF :: Box (O a) -> F (SigF a)
+ WidgetRattus.Future: mkSigF' :: Box (O a) -> O (F (SigF a))
+ WidgetRattus.Future: scan :: Stable b => Box (b -> a -> b) -> b -> SigF a -> SigF b
+ WidgetRattus.Future: scanAwait :: Stable b => Box (b -> a -> b) -> b -> F (SigF a) -> F (SigF b)
+ WidgetRattus.Future: switch :: SigF a -> F (SigF a) -> SigF a
+ WidgetRattus.Future: switchAwait :: F (SigF a) -> F (SigF a) -> F (SigF a)
+ WidgetRattus.Future: switchS :: Stable a => SigF a -> F (a -> SigF a) -> SigF a
+ WidgetRattus.Future: sync :: O (F a) -> O (F b) -> O (F a :* F b)
+ WidgetRattus.Future: syncF :: (Stable a, Stable b) => F a -> F b -> F (a :* b)
+ WidgetRattus.Future: trigger :: Stable b => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c
+ WidgetRattus.Future: triggerAwait :: Stable b => Box (a -> b -> c) -> F (SigF a) -> SigF b -> F (SigF c)
+ WidgetRattus.Future: zipWith :: (Stable a, Stable b) => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c
+ WidgetRattus.Future: zipWithAwait :: (Stable a, Stable b) => Box (a -> b -> c) -> a -> b -> F (SigF a) -> F (SigF b) -> F (SigF c)
+ WidgetRattus.InternalPrimitives: Both :: !a -> !b -> Select a b
+ WidgetRattus.InternalPrimitives: Box :: a -> Box a
+ WidgetRattus.InternalPrimitives: Chan :: InputChannelIdentifier -> Chan a
+ WidgetRattus.InternalPrimitives: Delay :: !Clock -> (InputValue -> a) -> O a
+ WidgetRattus.InternalPrimitives: Fst :: !a -> !O b -> Select a b
+ WidgetRattus.InternalPrimitives: Snd :: !O a -> !b -> Select a b
+ WidgetRattus.InternalPrimitives: [ContinuousData] :: Continuous a => !Weak (IORef a) -> ContinuousData
+ WidgetRattus.InternalPrimitives: [MoreInputs] :: !InputChannelIdentifier -> !a -> !InputValue -> InputValue
+ WidgetRattus.InternalPrimitives: [OneInput] :: !InputChannelIdentifier -> !a -> InputValue
+ WidgetRattus.InternalPrimitives: adv :: O a -> a
+ WidgetRattus.InternalPrimitives: adv' :: O a -> InputValue -> a
+ WidgetRattus.InternalPrimitives: box :: a -> Box a
+ WidgetRattus.InternalPrimitives: channelMember :: InputChannelIdentifier -> Clock -> Bool
+ WidgetRattus.InternalPrimitives: class Continuous p
+ WidgetRattus.InternalPrimitives: class Stable a
+ WidgetRattus.InternalPrimitives: clockUnion :: Clock -> Clock -> Clock
+ WidgetRattus.InternalPrimitives: data Box a
+ WidgetRattus.InternalPrimitives: data ContinuousData
+ WidgetRattus.InternalPrimitives: data InputValue
+ WidgetRattus.InternalPrimitives: data O a
+ WidgetRattus.InternalPrimitives: data Select a b
+ WidgetRattus.InternalPrimitives: defaultPromote :: Continuous a => a -> Box a
+ WidgetRattus.InternalPrimitives: delay :: a -> O a
+ WidgetRattus.InternalPrimitives: emptyClock :: Clock
+ WidgetRattus.InternalPrimitives: extractClock :: O a -> Clock
+ WidgetRattus.InternalPrimitives: inputInClock :: InputValue -> Clock -> Bool
+ WidgetRattus.InternalPrimitives: instance WidgetRattus.InternalPrimitives.Stable a => WidgetRattus.InternalPrimitives.Continuous a
+ WidgetRattus.InternalPrimitives: never :: O a
+ WidgetRattus.InternalPrimitives: newtype Chan a
+ WidgetRattus.InternalPrimitives: nextProgress :: Continuous p => p -> Clock
+ WidgetRattus.InternalPrimitives: progressAndNext :: Continuous p => InputValue -> p -> (p, Clock)
+ WidgetRattus.InternalPrimitives: progressInternal :: Continuous p => InputValue -> p -> p
+ WidgetRattus.InternalPrimitives: progressPromoteStore :: InputValue -> IO ()
+ WidgetRattus.InternalPrimitives: progressPromoteStoreAtomic :: InputValue -> IO ()
+ WidgetRattus.InternalPrimitives: progressPromoteStoreMutex :: MVar ()
+ WidgetRattus.InternalPrimitives: promote :: Continuous a => a -> Box a
+ WidgetRattus.InternalPrimitives: promoteInternal :: Continuous p => p -> Box p
+ WidgetRattus.InternalPrimitives: promoteStore :: IORef [ContinuousData]
+ WidgetRattus.InternalPrimitives: rattusError :: [Char] -> a
+ WidgetRattus.InternalPrimitives: select :: O a -> O b -> Select a b
+ WidgetRattus.InternalPrimitives: select' :: O a -> O b -> InputValue -> Select a b
+ WidgetRattus.InternalPrimitives: singletonClock :: InputChannelIdentifier -> Clock
+ WidgetRattus.InternalPrimitives: type Clock = IntSet
+ WidgetRattus.InternalPrimitives: type InputChannelIdentifier = Int
+ WidgetRattus.InternalPrimitives: unbox :: Box a -> a
+ WidgetRattus.Plugin: AllowLazyData :: WidgetRattus
+ WidgetRattus.Plugin: AllowRecursion :: WidgetRattus
+ WidgetRattus.Plugin: data WidgetRattus
+ WidgetRattus.Plugin: plugin :: Plugin
+ WidgetRattus.Plugin.Annotation: AllowLazyData :: WidgetRattus
+ WidgetRattus.Plugin.Annotation: AllowRecursion :: WidgetRattus
+ WidgetRattus.Plugin.Annotation: ExpectError :: InternalAnn
+ WidgetRattus.Plugin.Annotation: ExpectWarning :: InternalAnn
+ WidgetRattus.Plugin.Annotation: data InternalAnn
+ WidgetRattus.Plugin.Annotation: data WidgetRattus
+ WidgetRattus.Plugin.Annotation: instance Data.Data.Data WidgetRattus.Plugin.Annotation.InternalAnn
+ WidgetRattus.Plugin.Annotation: instance Data.Data.Data WidgetRattus.Plugin.Annotation.WidgetRattus
+ WidgetRattus.Plugin.Annotation: instance GHC.Classes.Eq WidgetRattus.Plugin.Annotation.InternalAnn
+ WidgetRattus.Plugin.Annotation: instance GHC.Classes.Eq WidgetRattus.Plugin.Annotation.WidgetRattus
+ WidgetRattus.Plugin.Annotation: instance GHC.Classes.Ord WidgetRattus.Plugin.Annotation.InternalAnn
+ WidgetRattus.Plugin.Annotation: instance GHC.Classes.Ord WidgetRattus.Plugin.Annotation.WidgetRattus
+ WidgetRattus.Plugin.Annotation: instance GHC.Show.Show WidgetRattus.Plugin.Annotation.InternalAnn
+ WidgetRattus.Plugin.Annotation: instance GHC.Show.Show WidgetRattus.Plugin.Annotation.WidgetRattus
+ WidgetRattus.Primitives: Both :: !a -> !b -> Select a b
+ WidgetRattus.Primitives: Fst :: !a -> !O b -> Select a b
+ WidgetRattus.Primitives: Snd :: !O a -> !b -> Select a b
+ WidgetRattus.Primitives: adv :: O a -> a
+ WidgetRattus.Primitives: box :: a -> Box a
+ WidgetRattus.Primitives: class Continuous p
+ WidgetRattus.Primitives: class Stable a
+ WidgetRattus.Primitives: data Box a
+ WidgetRattus.Primitives: data O a
+ WidgetRattus.Primitives: data Select a b
+ WidgetRattus.Primitives: delay :: a -> O a
+ WidgetRattus.Primitives: never :: O a
+ WidgetRattus.Primitives: promote :: Continuous a => a -> Box a
+ WidgetRattus.Primitives: select :: O a -> O b -> Select a b
+ WidgetRattus.Primitives: unbox :: Box a -> a
+ WidgetRattus.Signal: (:::) :: !a -> !O (Sig a) -> Sig a
+ WidgetRattus.Signal: buffer :: Stable a => a -> Sig a -> Sig a
+ WidgetRattus.Signal: bufferAwait :: Stable a => a -> O (Sig a) -> O (Sig a)
+ WidgetRattus.Signal: cond :: Stable a => Sig Bool -> Sig a -> Sig a -> Sig a
+ WidgetRattus.Signal: const :: a -> Sig a
+ WidgetRattus.Signal: current :: Sig a -> a
+ WidgetRattus.Signal: data Sig a
+ WidgetRattus.Signal: derivative :: forall a v. (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a) => Sig v -> Sig v
+ WidgetRattus.Signal: future :: Sig a -> O (Sig a)
+ WidgetRattus.Signal: infixr 5 :::
+ WidgetRattus.Signal: instance WidgetRattus.Channels.Producer (WidgetRattus.Signal.Sig a) a
+ WidgetRattus.Signal: instance WidgetRattus.Channels.Producer (WidgetRattus.Signal.SigMaybe a) a
+ WidgetRattus.Signal: instance WidgetRattus.InternalPrimitives.Continuous a => WidgetRattus.InternalPrimitives.Continuous (WidgetRattus.Signal.Sig a)
+ WidgetRattus.Signal: integral :: forall a v. (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a) => v -> Sig v -> Sig v
+ WidgetRattus.Signal: interleave :: Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)
+ WidgetRattus.Signal: interleaveAll :: Box (a -> a -> a) -> List (O (Sig a)) -> O (Sig a)
+ WidgetRattus.Signal: jump :: Box (a -> Maybe' (Sig a)) -> Sig a -> Sig a
+ WidgetRattus.Signal: jumping :: Box (a -> Maybe' (Sig a)) -> Sig a -> Sig a
+ WidgetRattus.Signal: map :: Box (a -> b) -> Sig a -> Sig b
+ WidgetRattus.Signal: mapAwait :: Box (a -> b) -> O (Sig a) -> O (Sig b)
+ WidgetRattus.Signal: mapInterleave :: Box (a -> a) -> Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)
+ WidgetRattus.Signal: mkBoxSig :: Box (O a) -> Box (O (Sig a))
+ WidgetRattus.Signal: mkSig :: Box (O a) -> O (Sig a)
+ WidgetRattus.Signal: scan :: Stable b => Box (b -> a -> b) -> b -> Sig a -> Sig b
+ WidgetRattus.Signal: scanAwait :: Stable b => Box (b -> a -> b) -> b -> O (Sig a) -> Sig b
+ WidgetRattus.Signal: scanAwaitC :: Stable b => Box (b -> a -> C b) -> b -> O (Sig a) -> C (Sig b)
+ WidgetRattus.Signal: scanC :: Stable b => Box (b -> a -> C b) -> b -> Sig a -> C (Sig b)
+ WidgetRattus.Signal: scanMap :: Stable b => Box (b -> a -> b) -> Box (b -> c) -> b -> Sig a -> Sig c
+ WidgetRattus.Signal: stop :: Box (a -> Bool) -> Sig a -> Sig a
+ WidgetRattus.Signal: switch :: Sig a -> O (Sig a) -> Sig a
+ WidgetRattus.Signal: switchAwait :: O (Sig a) -> O (Sig a) -> O (Sig a)
+ WidgetRattus.Signal: switchR :: Stable a => Sig a -> O (Sig (a -> Sig a)) -> Sig a
+ WidgetRattus.Signal: switchS :: Stable a => Sig a -> O (a -> Sig a) -> Sig a
+ WidgetRattus.Signal: trigger :: (Stable b, Stable c) => Box (a -> b -> c) -> Sig a -> Sig b -> Sig c
+ WidgetRattus.Signal: triggerAwait :: (Stable b, Stable c) => Box (a -> b -> c) -> c -> O (Sig a) -> Sig b -> Sig c
+ WidgetRattus.Signal: triggerAwaitM :: Stable b => Box (a -> b -> Maybe' c) -> O (Sig a) -> Sig b -> O (Sig (Maybe' c))
+ WidgetRattus.Signal: triggerM :: Stable b => Box (a -> b -> Maybe' c) -> Sig a -> Sig b -> Sig (Maybe' c)
+ WidgetRattus.Signal: update :: Stable a => Sig a -> O (Sig (a -> a)) -> Sig a
+ WidgetRattus.Signal: zip :: (Stable a, Stable b) => Sig a -> Sig b -> Sig (a :* b)
+ WidgetRattus.Signal: zipWith :: (Stable a, Stable b) => Box (a -> b -> c) -> Sig a -> Sig b -> Sig c
+ WidgetRattus.Signal: zipWith3 :: forall a b c d. (Stable a, Stable b, Stable c) => Box (a -> b -> c -> d) -> Sig a -> Sig b -> Sig c -> Sig d
+ WidgetRattus.Strict: (+++) :: List a -> List a -> List a
+ WidgetRattus.Strict: (:!) :: !a -> !List a -> List a
+ WidgetRattus.Strict: (:*) :: !a -> !b -> (:*) a b
+ WidgetRattus.Strict: -- structure <tt>l</tt>.
+ WidgetRattus.Strict: -- | The <a>Item</a> type function returns the type of items of the
+ WidgetRattus.Strict: Just' :: !a -> Maybe' a
+ WidgetRattus.Strict: Nil :: List a
+ WidgetRattus.Strict: Nothing' :: Maybe' a
+ WidgetRattus.Strict: class () => IsList l where {
+ WidgetRattus.Strict: concatMap' :: (a -> List b) -> List a -> List b
+ WidgetRattus.Strict: curry' :: ((a :* b) -> c) -> a -> b -> c
+ WidgetRattus.Strict: data List a
+ WidgetRattus.Strict: data Maybe' a
+ WidgetRattus.Strict: data a :* b
+ WidgetRattus.Strict: delete' :: Eq a => a -> List a -> List a
+ WidgetRattus.Strict: deleteBy' :: (a -> a -> Bool) -> a -> List a -> List a
+ WidgetRattus.Strict: filter' :: (a -> Bool) -> List a -> List a
+ WidgetRattus.Strict: fromList :: IsList l => [Item l] -> l
+ WidgetRattus.Strict: fromListN :: IsList l => Int -> [Item l] -> l
+ WidgetRattus.Strict: fromMaybe' :: a -> Maybe' a -> a
+ WidgetRattus.Strict: fst' :: (a :* b) -> a
+ WidgetRattus.Strict: infixr 2 :*
+ WidgetRattus.Strict: infixr 8 :!
+ WidgetRattus.Strict: init' :: List a -> List a
+ WidgetRattus.Strict: instance (Data.VectorSpace.VectorSpace v a, Data.VectorSpace.VectorSpace w a, GHC.Float.Floating a, GHC.Classes.Eq a) => Data.VectorSpace.VectorSpace (v WidgetRattus.Strict.:* w) a
+ WidgetRattus.Strict: instance (GHC.Classes.Eq a, GHC.Classes.Eq b) => GHC.Classes.Eq (a WidgetRattus.Strict.:* b)
+ WidgetRattus.Strict: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (a WidgetRattus.Strict.:* b)
+ WidgetRattus.Strict: instance (WidgetRattus.InternalPrimitives.Continuous a, WidgetRattus.InternalPrimitives.Continuous b) => WidgetRattus.InternalPrimitives.Continuous (a WidgetRattus.Strict.:* b)
+ WidgetRattus.Strict: instance Data.Foldable.Foldable WidgetRattus.Strict.List
+ WidgetRattus.Strict: instance Data.Traversable.Traversable WidgetRattus.Strict.List
+ WidgetRattus.Strict: instance GHC.Base.Functor ((WidgetRattus.Strict.:*) a)
+ WidgetRattus.Strict: instance GHC.Base.Functor WidgetRattus.Strict.List
+ WidgetRattus.Strict: instance GHC.Classes.Eq a => GHC.Classes.Eq (WidgetRattus.Strict.List a)
+ WidgetRattus.Strict: instance GHC.Classes.Eq a => GHC.Classes.Eq (WidgetRattus.Strict.Maybe' a)
+ WidgetRattus.Strict: instance GHC.Classes.Ord a => GHC.Classes.Ord (WidgetRattus.Strict.Maybe' a)
+ WidgetRattus.Strict: instance GHC.IsList.IsList (WidgetRattus.Strict.List a)
+ WidgetRattus.Strict: instance GHC.Show.Show a => GHC.Show.Show (WidgetRattus.Strict.List a)
+ WidgetRattus.Strict: instance GHC.Show.Show a => GHC.Show.Show (WidgetRattus.Strict.Maybe' a)
+ WidgetRattus.Strict: instance WidgetRattus.InternalPrimitives.Continuous a => WidgetRattus.InternalPrimitives.Continuous (WidgetRattus.Strict.List a)
+ WidgetRattus.Strict: instance WidgetRattus.InternalPrimitives.Continuous a => WidgetRattus.InternalPrimitives.Continuous (WidgetRattus.Strict.Maybe' a)
+ WidgetRattus.Strict: isJust' :: Maybe' a -> Bool
+ WidgetRattus.Strict: listToMaybe' :: List a -> Maybe' a
+ WidgetRattus.Strict: map' :: (a -> b) -> List a -> List b
+ WidgetRattus.Strict: mapMaybe' :: (a -> Maybe' b) -> List a -> List b
+ WidgetRattus.Strict: maybe' :: b -> (a -> b) -> Maybe' a -> b
+ WidgetRattus.Strict: nub' :: Eq a => List a -> List a
+ WidgetRattus.Strict: nubBy' :: (a -> a -> Bool) -> List a -> List a
+ WidgetRattus.Strict: readMaybe' :: Read a => Text -> Maybe' a
+ WidgetRattus.Strict: reverse' :: List a -> List a
+ WidgetRattus.Strict: singleton :: a -> List a
+ WidgetRattus.Strict: snd' :: (a :* b) -> b
+ WidgetRattus.Strict: splitOn' :: Text -> Text -> List Text
+ WidgetRattus.Strict: toList :: IsList l => l -> [Item l]
+ WidgetRattus.Strict: toText :: Show a => a -> Text
+ WidgetRattus.Strict: type family Item l;
+ WidgetRattus.Strict: uncurry' :: (a -> b -> c) -> (a :* b) -> c
+ WidgetRattus.Strict: union' :: Eq a => List a -> List a -> List a
+ WidgetRattus.Strict: unionBy' :: (a -> a -> Bool) -> List a -> List a -> List a
+ WidgetRattus.Strict: zip' :: List a -> List b -> List (a :* b)
+ WidgetRattus.Strict: zipWith' :: (a -> b -> c) -> List a -> List b -> List c
+ WidgetRattus.Strict: }
+ WidgetRattus.Widgets: addInputSigTF :: TextField -> O (Sig Text) -> TextField
+ WidgetRattus.Widgets: btnClick :: Button -> Chan ()
+ WidgetRattus.Widgets: btnContent :: Button -> Sig a
+ WidgetRattus.Widgets: btnOnClick :: Button -> Box (O ())
+ WidgetRattus.Widgets: btnOnClickSig :: Button -> O (Sig ())
+ WidgetRattus.Widgets: class Stable a => Displayable a
+ WidgetRattus.Widgets: class Continuous a => IsWidget a
+ WidgetRattus.Widgets: class Widgets ws
+ WidgetRattus.Widgets: data Button
+ WidgetRattus.Widgets: data HStack
+ WidgetRattus.Widgets: data Label
+ WidgetRattus.Widgets: data Popup
+ WidgetRattus.Widgets: data Slider
+ WidgetRattus.Widgets: data TextDropdown
+ WidgetRattus.Widgets: data TextField
+ WidgetRattus.Widgets: data VStack
+ WidgetRattus.Widgets: data Widget
+ WidgetRattus.Widgets: display :: Displayable a => a -> Text
+ WidgetRattus.Widgets: instance (WidgetRattus.Widgets.Widgets w, WidgetRattus.Widgets.Widgets v) => WidgetRattus.Widgets.Widgets (w WidgetRattus.Strict.:* v)
+ WidgetRattus.Widgets: instance WidgetRattus.Widgets.InternalTypes.Displayable Data.Text.Internal.Text
+ WidgetRattus.Widgets: instance WidgetRattus.Widgets.InternalTypes.Displayable GHC.Types.Int
+ WidgetRattus.Widgets: instance WidgetRattus.Widgets.InternalTypes.IsWidget w => WidgetRattus.Widgets.Widgets w
+ WidgetRattus.Widgets: instance WidgetRattus.Widgets.Widgets w => WidgetRattus.Widgets.Widgets (WidgetRattus.Strict.List w)
+ WidgetRattus.Widgets: labText :: Label -> Sig a
+ WidgetRattus.Widgets: mkButton :: Displayable a => Sig a -> C Button
+ WidgetRattus.Widgets: mkConstHStack :: Widgets ws => ws -> C HStack
+ WidgetRattus.Widgets: mkConstVStack :: Widgets ws => ws -> C VStack
+ WidgetRattus.Widgets: mkHStack :: IsWidget a => Sig (List a) -> C HStack
+ WidgetRattus.Widgets: mkLabel :: Displayable a => Sig a -> C Label
+ WidgetRattus.Widgets: mkPopup :: Sig Bool -> Sig Widget -> C Popup
+ WidgetRattus.Widgets: mkProgressBar :: Sig Int -> Sig Int -> Sig Int -> C Slider
+ WidgetRattus.Widgets: mkSlider :: Int -> Sig Int -> Sig Int -> C Slider
+ WidgetRattus.Widgets: mkTextDropdown :: Sig (List Text) -> Text -> C TextDropdown
+ WidgetRattus.Widgets: mkTextField :: Text -> C TextField
+ WidgetRattus.Widgets: mkVStack :: IsWidget a => Sig (List a) -> C VStack
+ WidgetRattus.Widgets: mkWidget :: IsWidget a => a -> Widget
+ WidgetRattus.Widgets: mkWidgetNode :: IsWidget a => a -> WidgetNode AppModel AppEvent
+ WidgetRattus.Widgets: popChild :: Popup -> Sig Widget
+ WidgetRattus.Widgets: popCurr :: Popup -> Sig Bool
+ WidgetRattus.Widgets: popEvent :: Popup -> Chan Bool
+ WidgetRattus.Widgets: runApplication :: IsWidget a => C a -> IO ()
+ WidgetRattus.Widgets: setEnabled :: IsWidget a => a -> Sig Bool -> Widget
+ WidgetRattus.Widgets: setInputSigTF :: TextField -> Sig Text -> TextField
+ WidgetRattus.Widgets: sldCurr :: Slider -> Sig Int
+ WidgetRattus.Widgets: sldEvent :: Slider -> Chan Int
+ WidgetRattus.Widgets: sldMax :: Slider -> Sig Int
+ WidgetRattus.Widgets: sldMin :: Slider -> Sig Int
+ WidgetRattus.Widgets: tddCurr :: TextDropdown -> Sig Text
+ WidgetRattus.Widgets: tddEvent :: TextDropdown -> Chan Text
+ WidgetRattus.Widgets: tddList :: TextDropdown -> Sig (List Text)
+ WidgetRattus.Widgets: textFieldOnInput :: TextField -> Box (O Text)
+ WidgetRattus.Widgets: textFieldOnInputSig :: TextField -> O (Sig Text)
+ WidgetRattus.Widgets: tfContent :: TextField -> Sig Text
+ WidgetRattus.Widgets: tfInput :: TextField -> Chan Text
+ WidgetRattus.Widgets: toWidgetList :: Widgets ws => ws -> List Widget
Files
- CHANGELOG.md +5/−0
- WidgetRattus.cabal +43/−29
- docs/paper.pdf binary
- examples/gui/src/Calculator.hs +99/−0
- examples/gui/src/Counter.hs +26/−0
- examples/gui/src/FlightBooker.hs +88/−0
- examples/gui/src/TemperatureConverter.hs +50/−0
- examples/gui/src/Timer.hs +67/−0
- src/AsyncRattus.hs +0/−27
- src/AsyncRattus/Channels.hs +0/−211
- src/AsyncRattus/Derive.hs +0/−117
- src/AsyncRattus/Future.hs +0/−219
- src/AsyncRattus/InternalPrimitives.hs +0/−263
- src/AsyncRattus/Plugin.hs +0/−162
- src/AsyncRattus/Plugin/Annotation.hs +0/−30
- src/AsyncRattus/Plugin/CheckClockCompatibility.hs +0/−261
- src/AsyncRattus/Plugin/Dependency.hs +0/−385
- src/AsyncRattus/Plugin/PrimExpr.hs +0/−109
- src/AsyncRattus/Plugin/ScopeCheck.hs +0/−812
- src/AsyncRattus/Plugin/SingleTick.hs +0/−226
- src/AsyncRattus/Plugin/StableSolver.hs +0/−83
- src/AsyncRattus/Plugin/Strictify.hs +0/−64
- src/AsyncRattus/Plugin/Transform.hs +0/−143
- src/AsyncRattus/Plugin/Utils.hs +0/−380
- src/AsyncRattus/Primitives.hs +0/−21
- src/AsyncRattus/Signal.hs +0/−412
- src/AsyncRattus/Strict.hs +0/−255
- src/WidgetRattus.hs +30/−0
- src/WidgetRattus/Channels.hs +79/−0
- src/WidgetRattus/Derive.hs +117/−0
- src/WidgetRattus/Future.hs +218/−0
- src/WidgetRattus/InternalPrimitives.hs +264/−0
- src/WidgetRattus/Plugin.hs +162/−0
- src/WidgetRattus/Plugin/Annotation.hs +30/−0
- src/WidgetRattus/Plugin/CheckClockCompatibility.hs +261/−0
- src/WidgetRattus/Plugin/Dependency.hs +385/−0
- src/WidgetRattus/Plugin/PrimExpr.hs +109/−0
- src/WidgetRattus/Plugin/ScopeCheck.hs +812/−0
- src/WidgetRattus/Plugin/SingleTick.hs +226/−0
- src/WidgetRattus/Plugin/StableSolver.hs +83/−0
- src/WidgetRattus/Plugin/Strictify.hs +64/−0
- src/WidgetRattus/Plugin/Transform.hs +143/−0
- src/WidgetRattus/Plugin/Utils.hs +384/−0
- src/WidgetRattus/Primitives.hs +21/−0
- src/WidgetRattus/Signal.hs +492/−0
- src/WidgetRattus/Strict.hs +275/−0
- src/WidgetRattus/Widgets.hs +217/−0
- src/WidgetRattus/Widgets/InternalTypes.hs +122/−0
- test/IllTyped.hs +3/−3
- test/WellTyped.hs +3/−3
CHANGELOG.md view
@@ -1,3 +1,8 @@+# 0.3++- Include the Widgets library.+- Replace module names from AsyncRattus to WidgetRattus+ # 0.2 Extend continuous types so that they can track their channel
WidgetRattus.cabal view
@@ -1,27 +1,37 @@ cabal-version: 1.18 name: WidgetRattus-version: 0.2+version: 0.3 category: FRP-synopsis: An asynchronous modal FRP language+synopsis: An asynchronous modal FRP language for GUI programming description: This library implements an experimental variant of the Async Rattus programming language that features extensions for implementing GUIs.+ A comprehensive introduction can be found in this + <src/docs/paper.pdf paper>.++ .++ Several example GUIs implemented using Widget Rattus can be found+ <https://github.com/pa-ba/AsyncRattus/tree/WidgetRattus/examples/gui here>. -homepage: https://github.com/pa-ba/AsyncRattus/+homepage: https://github.com/pa-ba/AsyncRattus/tree/WidgetRattus bug-reports: https://github.com/pa-ba/AsyncRattus/issues License: BSD3 License-file: LICENSE-copyright: Copyright (C) 2023 Emil Houlborg, Gregers Rørdam, Patrick Bahr-Author: Emil Houlborg, Gregers Rørdam, Patrick Bahr+copyright: Copyright (C) 2024 Emil Houlborg, Gregers Rørdam, Patrick Bahr, Jean-Claude Sebastian Disch, Asger Lademark Heegaard+Author: Emil Houlborg, Gregers Rørdam, Patrick Bahr, Jean-Claude Sebastian Disch, Asger Lademark Heegaard maintainer: Patrick Bahr <paba@itu.dk> stability: experimental build-type: Custom extra-source-files: CHANGELOG.md+ examples/gui/src/*.hs+extra-doc-files: docs/paper.pdf+ custom-setup setup-depends:@@ -30,34 +40,38 @@ library- exposed-modules: AsyncRattus- AsyncRattus.Signal- AsyncRattus.Future- AsyncRattus.Strict- AsyncRattus.Plugin- AsyncRattus.Primitives- AsyncRattus.InternalPrimitives- AsyncRattus.Channels- AsyncRattus.Plugin.Annotation+ exposed-modules: WidgetRattus+ WidgetRattus.Signal+ WidgetRattus.Future+ WidgetRattus.Strict+ WidgetRattus.Plugin+ WidgetRattus.Primitives+ WidgetRattus.InternalPrimitives+ WidgetRattus.Channels+ WidgetRattus.Plugin.Annotation+ WidgetRattus.Widgets - other-modules: AsyncRattus.Plugin.ScopeCheck- AsyncRattus.Plugin.SingleTick- AsyncRattus.Plugin.CheckClockCompatibility- AsyncRattus.Plugin.Strictify- AsyncRattus.Plugin.Utils- AsyncRattus.Plugin.Dependency- AsyncRattus.Plugin.StableSolver- AsyncRattus.Plugin.Transform- AsyncRattus.Plugin.PrimExpr- AsyncRattus.Derive+ other-modules: WidgetRattus.Plugin.ScopeCheck+ WidgetRattus.Plugin.SingleTick+ WidgetRattus.Plugin.CheckClockCompatibility+ WidgetRattus.Plugin.Strictify+ WidgetRattus.Plugin.Utils+ WidgetRattus.Plugin.Dependency+ WidgetRattus.Plugin.StableSolver+ WidgetRattus.Plugin.Transform+ WidgetRattus.Plugin.PrimExpr+ WidgetRattus.Widgets.InternalTypes+ WidgetRattus.Derive build-depends: base >=4.16 && <5, containers >= 0.6.5 && < 0.8,- ghc >= 9.2 && < 9.9,- ghc-boot >= 9.2 && < 9.9,+ ghc >= 9.2 && < 9.7,+ ghc-boot >= 9.2 && < 9.7, hashtables >= 1.3.1 && < 1.4, simple-affine-space >= 0.2.1 && < 0.3, transformers >= 0.5.6 && < 0.7,- template-haskell >= 2.17 && < 2.22+ template-haskell >= 2.17 && < 2.23,+ text >= 1.2 && < 3,+ monomer >= 1.4 && < 2 hs-source-dirs: src default-language: Haskell2010 ghc-options: -W@@ -69,7 +83,7 @@ main-is: test/IllTyped.hs default-language: Haskell2010 build-depends: WidgetRattus, base- ghc-options: -fplugin=AsyncRattus.Plugin+ ghc-options: -fplugin=WidgetRattus.Plugin Test-Suite well-typed@@ -78,5 +92,5 @@ hs-source-dirs: test default-language: Haskell2010 build-depends: WidgetRattus, base, containers, text- ghc-options: -fplugin=AsyncRattus.Plugin+ ghc-options: -fplugin=WidgetRattus.Plugin
+ docs/paper.pdf view
binary file changed (absent → 592894 bytes)
+ examples/gui/src/Calculator.hs view
@@ -0,0 +1,99 @@+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++import WidgetRattus+import WidgetRattus.Signal+import WidgetRattus.Widgets+import Prelude hiding (map, const, zipWith, zip, filter, getLine, putStrLn,null)+import Data.Text (Text)++nums :: List Int+nums = [0..9]++data Op = Plus | Minus | Equals | Reset++compute :: (Int :* Op :* Bool -> Maybe' (Int :* Op) -> Int :* Op :* Bool)+compute (n :* op :* _) Nothing' = (n :* op :* False)+compute _ (Just' (_ :* Reset)) = (0 :* Reset :* True)+compute (n :* Plus :* _) (Just' (m :* op)) = (n + m) :* op :* True+compute (n :* Minus :* _) (Just' (m :* op)) = (n - m) :* op :* True+compute (_ :* Equals :* _) (Just' (m :* op)) = m :* op :* True+compute (_ :* Reset :* _) (Just' (m :* op)) = m :* op :* True+++window :: C VStack+window = do++ -- construct number buttons+ numBtns :: List Button + <- mapM (mkButton . const) nums+ let [b0, b1, b2, b3, b4, b5, b6, b7, b8, b9] = numBtns+ -- construct operator buttons+ resetBut <- mkButton (const ("C"::Text))+ addBut <- mkButton (const ("+"::Text))+ subBut <- mkButton (const ("-"::Text))+ eqBut <- mkButton (const ("="::Text))++ -- signal to construct numbers+ let numClicks :: List (O (Sig (Int -> Int))) + = zipWith' (\b n -> mapAwait (box (\ _ x -> x * 10 + n)) (btnOnClickSig b)) numBtns nums+ +++ -- signal to reset the current number to 0, after clicking an+ -- operator button+ let resetSig :: O (Sig (Int -> Int))+ = mapAwait (box (\ _ _ -> 0))+ $ interleaveAll (box (\ a _ -> a))+ $ map' btnOnClickSig [addBut, subBut, eqBut,resetBut]++ -- combine signals to construct the number signal+ let sigList = resetSig :! numClicks :: List (O (Sig (Int -> Int)))+ let combinedSig = interleaveAll (box (\ a _ -> a)) sigList++ -- number signal (i.e. the multidigit number that has been+ -- constructed)+ let numberSig :: Sig Int+ = scanAwait (box (\ a f-> f a)) 0 combinedSig+ -- operator signal+ let opSig :: O (Sig Op)+ = interleaveAll (box (\ a _ -> a))+ $ map' (\ (op :* btn) -> mapAwait (box (\ _ -> op)) (btnOnClickSig btn) )+ [(Plus :* addBut), (Minus :* subBut), (Equals :* eqBut), (Reset :* resetBut)]++ -- signal consisting of an operand (i.e. a number) @n@ and an+ -- operator @op@. @n@ is the value of @numberSig@ just before+ -- clicking an operator button, and op is taken from opSig+ let operand :: Sig (Maybe' (Int :* Op))+ = Nothing' ::: triggerAwaitM (box (\op n -> Just' (n :* op))) opSig (buffer 0 numberSig)++ -- The result signal consisting of a number n that is the result+ -- of the current computation, an operator op that still needs to+ -- applied to n and a Boolean b that indicates whether we have+ -- just calculated n (and thus n should be displayed)+ let resSig :: Sig (Int :* Op :* Bool)+ = scan (box compute) (0 :* Plus :* True) operand+ -- The signal that should be displayed+ let displaySig :: Sig Int+ = zipWith (box (\ (n :* _ :* b) m -> if b then n else m)) resSig numberSig+ + -- label to display the result (and operands)+ result <- mkLabel displaySig+ + -- lay out widgets+ operators <- mkConstVStack (resetBut :* addBut :* subBut :* eqBut)+ row1 <- mkConstHStack (b7 :* b8 :* b9)+ row2 <- mkConstHStack (b4 :* b5 :* b6)+ row3 <- mkConstHStack (b1 :* b2 :* b3)++ numbers <- mkConstVStack (row1 :* row2 :* row3 :* b0)++ input <- mkConstHStack (numbers :* operators)++ mkConstVStack (result :* input)++main :: IO ()+main = runApplication window
+ examples/gui/src/Counter.hs view
@@ -0,0 +1,26 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}+{-# HLINT ignore "Evaluate" #-}+{-# HLINT ignore "Use const" #-}+{-# LANGUAGE ScopedTypeVariables #-}++import WidgetRattus+import WidgetRattus.Signal+import WidgetRattus.Widgets++import Prelude hiding (map, const, zipWith, zip, filter, getLine, putStrLn,null)+import Data.Text hiding (filter, map, all)++window :: C VStack+window = do + btn <- mkButton (const ("Increment" :: Text))+ let sig = btnOnClickSig btn+ let sig' = scanAwait (box (\ n _ -> n+1 :: Int)) 0 sig + lbl <- mkLabel sig'+ mkConstVStack (lbl :* btn)+ ++main :: IO ()+main = runApplication window
+ examples/gui/src/FlightBooker.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}+{-# HLINT ignore "Evaluate" #-}+{-# HLINT ignore "Use const" #-}++import WidgetRattus+import WidgetRattus.Signal+import WidgetRattus.Widgets+import Prelude hiding (map, const, zipWith, zipWith3, zip, filter, getLine, putStrLn,null)+import Data.Text (Text)++-- Benchmark 3+isDate :: Text -> Bool+isDate txt = case splitOn' "-" txt of+ [dayStr, monthStr, yearStr] ->+ let day = readMaybe' dayStr+ month = readMaybe' monthStr+ year = readMaybe' yearStr+ in isValid day month year+ _ -> False+ where+ isValid :: Maybe' Int -> Maybe' Int -> Maybe' Int -> Bool+ isValid (Just' d) (Just' m) (Just' y)+ | m < 1 || m > 12 = False+ | d < 1 || d > daysInMonth m y = False+ | otherwise = True+ isValid _ _ _ = False++ daysInMonth :: Int -> Int -> Int+ daysInMonth m y+ | m `elem` ([4, 6, 9, 11] :: List Int) = 30+ | m == 2 = if isLeapYear y then 29 else 28+ | otherwise = 31++ isLeapYear :: Int -> Bool+ isLeapYear y = y `mod` 4 == 0 && (y `mod` 100 /= 0 || y `mod` 400 == 0)++isLater :: Text -> Text -> Bool+isLater dep ret = case (splitOn' "-" dep, splitOn' "-" ret) of+ ([depDayStr, depMonthStr, depYearStr], [retDayStr, retMonthStr, retYearStr]) ->+ let depDay = readMaybe' depDayStr+ depMonth = readMaybe' depMonthStr+ depYear = readMaybe' depYearStr+ retDay = readMaybe' retDayStr+ retMonth = readMaybe' retMonthStr+ retYear = readMaybe' retYearStr+ in all isJust' ([depDay, depMonth, depYear, retDay, retMonth, retYear] :: List (Maybe' Int)) &&+ (depYear < retYear ||+ (depYear == retYear && (depMonth < retMonth ||+ (depMonth == retMonth && depDay < retDay))))+ _ -> False++bookingToText :: Bool -> Text -> Text -> Text+bookingToText oneWay dep ret =+ "You have booked a " <> if oneWay then "one-way flight on " <> dep+ else "return flight from " <> dep <> " to " <> ret++window :: C VStack+window = do+ dropDown <- mkTextDropdown (const ["One-Way", "Return-Flight"]) "One-Way"+ tf1 <- mkTextField "01-01-2021"+ tf2 <- mkTextField "01-02-2021"+ button <- mkButton (const ("Book" :: Text))++ let isRF = map (box (== "Return-Flight")) (tddCurr dropDown)+ let isOW = map (box (== "One-Way")) (tddCurr dropDown)+ + let labelSig = zipWith3 (box bookingToText) isOW (tfContent tf1) (tfContent tf2)++ let sig = scanAwait (box (\ _ _ -> True )) False (btnOnClickSig button)++ label <- mkLabel labelSig+ + popup <- mkPopup sig (const (mkWidget label))++ let tf1IsDate = map (box isDate) (tfContent tf1)+ let tf1IsLater = zipWith (box isLater) (tfContent tf1) (tfContent tf2)++ let oneWayAndDate = zipWith (box (&&)) isOW tf1IsDate+ let returnFlightAndIsLater = zipWith (box (&&)) isRF tf1IsLater+ let validBooking = zipWith (box (||)) oneWayAndDate returnFlightAndIsLater++ mkConstVStack (popup :* dropDown :* tf1 :* setEnabled tf2 isRF :* setEnabled button validBooking)++main :: IO ()+main = runApplication window
+ examples/gui/src/TemperatureConverter.hs view
@@ -0,0 +1,50 @@+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}+{-# HLINT ignore "Evaluate" #-}+{-# HLINT ignore "Use const" #-}+++import WidgetRattus+import WidgetRattus.Signal+import WidgetRattus.Widgets+import Prelude hiding (map, const, zipWith, zip, filter, getLine, putStrLn,null)+import Data.Text hiding (filter, map, all)+import Data.Text.Read++-- Benchmark 2+celsiusToFahrenheit :: Text -> Text+celsiusToFahrenheit t =+ case signed decimal t of+ Right (t', _) -> toText (t' * 9 `div` 5 + 32)+ Left _ -> "Invalid input"++fahrenheitToCelsius :: Text -> Text+fahrenheitToCelsius t =+ case signed decimal t of+ Right (t', _) -> toText ((t' - 32) * 5 `div` 9)+ Left _ -> "Invalid input"++window :: C HStack+window = do+ tfF1 <- mkTextField "32"+ tfC1 <- mkTextField "0"++ let convertFtoC = map (box fahrenheitToCelsius) (tfContent tfF1)+ let convertCtoF = map (box celsiusToFahrenheit) (tfContent tfC1)++ let tfF2 = addInputSigTF tfF1 (future convertCtoF)+ let tfC2 = addInputSigTF tfC1 (future convertFtoC)++ fLabel <- mkLabel (const ("Fahrenheit" :: Text))+ cLabel <- mkLabel (const ("Celsius" :: Text))++ fStack <- mkConstVStack (tfF2 :* fLabel)+ cStack <- mkConstVStack (tfC2 :* cLabel) + mkConstHStack (fStack :* cStack)+ ++main :: IO ()+main = runApplication window
+ examples/gui/src/Timer.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedLists #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}++{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+++import WidgetRattus+import WidgetRattus.Signal+import WidgetRattus.Widgets+import Prelude hiding (map, const, zipWith, zip, filter, getLine, putStrLn,null)+import Data.Text hiding (filter, map, all)+++everySecond :: Box (O())+everySecond = timer 1000000++everySecondSig :: O (Sig ())+everySecondSig = mkSig everySecond++nats :: (Int :* Int) -> Sig (Int :* Int)+nats (n :* max) = stop + (box (\ (n :* max) -> n >= max)) + (scanAwait (box (\ (n :* max) _ -> (n + 1) :* max)) (n :* max) everySecondSig)+++reset :: (Int :* Int) -> (Int :* Int)+reset (_ :* max) = (0 :* max)++setMax :: Int -> (Int :* Int) -> (Int :* Int)+setMax max' (n :* _) = ((min n max') :* max')++window :: C VStack+window = do+ slider <- mkSlider 50 (const 1) (const 100)+ resetBtn <- mkButton (const ("Reset" :: Text))++ let resSig :: O (Sig ()) + = mkSig (btnOnClick resetBtn)+ let resetSig :: O (Sig (Int :* Int -> Int :* Int))+ = mapAwait (box (\ _ -> reset)) resSig++ let currentMax :: Int+ = current (sldCurr slider)+ let setMaxSig :: O (Sig (Int :* Int -> Int :* Int)) + = mapAwait (box setMax) (future (sldCurr slider))+ + let inputSig :: O (Sig (Int :* Int -> Int :* Int))+ = interleave (box (.)) resetSig setMaxSig++ let inputSig' :: O (Sig (Int :* Int -> Sig (Int :* Int)))+ = mapAwait (box (nats .)) inputSig++ let counterSig :: Sig (Int :* Int)+ = switchR (nats (0 :* currentMax)) inputSig'+ + let currentSig = map (box fst') counterSig+ let maxSig = map (box snd') counterSig++ label <- mkLabel currentSig+ pb <- mkProgressBar (const 0) maxSig currentSig++ mkConstVStack (slider :* resetBtn :* label :* pb)++main :: IO ()+main = runApplication window
− src/AsyncRattus.hs
@@ -1,27 +0,0 @@-{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}----- | The bare-bones Asynchronous Rattus language. To program with streams,--- you can use "AsyncRattus.Stream".--module AsyncRattus (- -- * Asynchronous Rattus language primitives- module AsyncRattus.Primitives,- -- * Strict data types- module AsyncRattus.Strict,- -- * Derive class instance declarations- module AsyncRattus.Derive,- -- * Annotation- AsyncRattus(..),- -- * other- mapO- )- where--import AsyncRattus.Plugin-import AsyncRattus.Strict-import AsyncRattus.Primitives-import AsyncRattus.Derive--mapO :: Box (a -> b) -> O a -> O b-mapO f later = delay (unbox f (adv later))
− src/AsyncRattus/Channels.hs
@@ -1,211 +0,0 @@-{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}--{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---- | This module is meant for library authors that want to build APIs--- for interacting with asynchronous resources, e.g. a GUI framework. --module AsyncRattus.Channels (- getInput,- setOutput,- mkInput,- startEventLoop,- timer,- Producer (..),- chan,- C (..),- delayC,- wait,- Chan-) where-import AsyncRattus.InternalPrimitives--import AsyncRattus.Plugin.Annotation-import AsyncRattus.Strict-import Control.Monad-import System.IO.Unsafe-import Data.IORef-import Unsafe.Coerce-import qualified Data.HashTable.IO as H-import Data.HashTable.IO (BasicHashTable)-import qualified Data.IntSet as IntSet-import Control.Concurrent hiding (Chan)---- | A type @p@ satisfying @Producer p a@ is essentially a signal that--- produces values of type @a@ but it might not produce such values at--- each tick.-class Producer p a | p -> a where- -- | Get the current value of the producer if any.- getCurrent :: p -> Maybe' a- -- | Get the next state of the producer. Morally, the type of this- -- method should be- --- -- > getNext :: p -> (exists q. Producer q a => O q)- --- -- We encode the existential type using continuation-passing style.- getNext :: p -> (forall q. Producer q a => O q -> b) -> b--instance Producer p a => Producer (O p) a where- getCurrent _ = Nothing'- getNext p cb = cb p--instance Producer p a => Producer (Box p) a where- getCurrent p = getCurrent (unbox p)- getNext p cb = getNext (unbox p) cb--newtype C a = C {unC :: IO a} deriving (Functor, Applicative, Monad)--chan :: C (Chan a)-chan = C (Chan <$> atomicModifyIORef nextFreshChannel (\ x -> (x - 1, x)))--delayC :: O (C a) -> C (O a)-delayC d = return (delay (unsafePerformIO (unC (adv d))))--{-# ANN wait AllowRecursion #-}-wait :: Chan a -> O a-wait (Chan ch) = Delay (singletonClock ch) (lookupInp ch) --{-# NOINLINE nextFreshChannel #-}-nextFreshChannel :: IORef InputChannelIdentifier-nextFreshChannel = unsafePerformIO (newIORef (-1))---{-# NOINLINE inputValue #-}-inputValue :: MVar (Maybe' InputValue)-inputValue = unsafePerformIO (newMVar Nothing')--{-# NOINLINE inputSem #-}-inputSem :: MVar ()-inputSem = unsafePerformIO newEmptyMVar--data OutputChannel where- OutputChannel :: Producer p a => !(O p) -> !(a -> IO ()) -> OutputChannel---{-# NOINLINE output #-}-output :: BasicHashTable InputChannelIdentifier (List (IORef (Maybe' OutputChannel)))-output = unsafePerformIO (H.new)--{-# NOINLINE eventLoopStarted #-}-eventLoopStarted :: IORef Bool-eventLoopStarted = unsafePerformIO (newIORef False)----- | This function can be used to implement input signals. It returns--- a boxed delayed computation @s@ and a callback function @cb@. The--- signal @mkSig s@ will produce a new value @v@ whenever the callback--- function @cb@ is called with argument @v@.-getInput :: IO (Box (O a) :* (a -> IO ()))-getInput = do ch <- atomicModifyIORef nextFreshChannel (\ x -> (x - 1, x))- return ((box (Delay (singletonClock ch) (lookupInp ch)))- :* \ x -> newInput ch x)---newInput :: InputChannelIdentifier -> a -> IO ()-newInput ch x = do iv <- takeMVar inputValue- case iv of - Nothing' -> putMVar inputValue (Just' (OneInput ch x)) >> putMVar inputSem ()- Just' more -> putMVar inputValue (Just' (MoreInputs ch x more))--{-# ANN lookupInp AllowRecursion #-}-lookupInp :: InputChannelIdentifier -> InputValue -> a-lookupInp _ (OneInput _ v) = unsafeCoerce v-lookupInp ch (MoreInputs ch' v more) = if ch' == ch then unsafeCoerce v else lookupInp ch more--{-# ANN setOutput' AllowLazyData #-}-setOutput' :: Producer p a => (a -> IO ()) -> O p -> IO ()-setOutput' cb !sig = do- ref <- newIORef (Just' (OutputChannel sig cb))- let upd Nothing = (Just (ref :! Nil),())- upd (Just ls) = (Just (ref :! ls),())- let upd' ch Nothing = do- forkIO (threadDelay ch >> newInput ch ())- return (Just (ref :! Nil),())- upd' _ (Just ls) = return (Just (ref :! ls),())- let run pre ch =- if ch > 0 then- pre >> H.mutateIO output ch (upd' ch)- else - pre >> H.mutate output ch upd- IntSet.foldl' run (return ()) (extractClock sig)----- | This function can be used to produces outputs. Given a signal @s@--- and function @f@, the call @setOutput s f@ registers @f@ as a--- callback function that is called with argument @v@ whenever the--- signal produces a new value @v@. For this function to work,--- 'startEventLoop' must be called.-setOutput :: Producer p a => p -> (a -> IO ()) -> IO ()-setOutput !sig cb = do- case getCurrent sig of- Just' cur' -> cb cur'- Nothing' -> return ()- getNext sig (setOutput' cb)---- | This function is essentially the composition of 'getInput' and--- 'setOutput'. It turns any producer into a signal.-mkInput :: Producer p a => p -> IO (Box (O a))-mkInput p = do (out :* cb) <- getInput- setOutput p cb- return out---- | @timer n@ produces a delayed computation that ticks every @n@--- milliseconds. In particular @mkSig (timer n)@ is a signal that--- produces a new value every #n# milliseconds.-timer :: Int -> Box (O ())-timer d = Box (Delay (singletonClock (d `max` 10)) (\ _ -> ()))---update :: InputValue -> IORef (Maybe' OutputChannel) -> IO ()-update inp ref = do- mout <- readIORef ref- case mout of- Nothing' -> return ()- Just' (OutputChannel (Delay _ sigf) cb) -> do- writeIORef ref Nothing'- let new = sigf inp- case getCurrent new of- Just' w' -> cb w'- Nothing' -> return ()- getNext new (setOutput' cb)---{-# ANN getOutputsForInputs AllowRecursion #-}-{-# ANN getOutputsForInputs AllowLazyData #-}-getOutputsForInputs :: List (IORef (Maybe' OutputChannel)) -> InputValue -> IO (List (IORef (Maybe' OutputChannel)))-getOutputsForInputs acc (OneInput ch _) = do res <- H.lookup output ch- case res of - Nothing -> return acc- Just ls -> H.delete output ch >> return (acc `union'` ls)-getOutputsForInputs acc (MoreInputs ch _ more) = do res <- H.lookup output ch- case res of - Nothing -> getOutputsForInputs acc more- Just ls -> H.delete output ch >> getOutputsForInputs (acc `union'` ls) more--{-# ANN eventLoop AllowRecursion #-}-{-# ANN eventLoop AllowLazyData #-}--eventLoop :: IO ()-eventLoop = do _ <- takeMVar inputSem- minp <- takeMVar inputValue- putMVar inputValue Nothing'- case minp of- Nothing' -> error "AsyncRattus.Channels.eventLoop unexpected state"- Just' inp -> do- ls <- getOutputsForInputs Nil inp- mapM_ (update inp) ls- eventLoop---- | In order for 'setOutput' to work, this IO action must be invoked.-startEventLoop :: IO ()-startEventLoop = do- started <- atomicModifyIORef eventLoopStarted (\b -> (True,b))- when (not started) eventLoop
− src/AsyncRattus/Derive.hs
@@ -1,117 +0,0 @@-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE ExistentialQuantification #-}---module AsyncRattus.Derive (continuous) where--import AsyncRattus.InternalPrimitives-import Language.Haskell.TH-import Language.Haskell.TH.Syntax-import Control.Monad---data DataInfo = forall flag . DataInfo Cxt Name [TyVarBndr flag] [Con] [DerivClause] --{-|- This function provides a list (of the given length) of new names based- on the given string.--}-newNames :: Int -> String -> Q [Name]-newNames n name = replicateM n (newName name)---{-|- This is the @Q@-lifted version of 'abstractNewtype.--}-abstractNewtypeQ :: Q Info -> Q (Maybe DataInfo)-abstractNewtypeQ = liftM abstractNewtype---{-| Apply a class name to type arguments to construct a type class- constraint.--}--mkClassP :: Name -> [Type] -> Type-mkClassP name = foldl AppT (ConT name)---{-| This function provides the name and the arity of the given data-constructor, and if it is a GADT also its type.--}-normalCon :: Con -> [(Name,[StrictType], Maybe Type)]-normalCon (NormalC constr args) = [(constr, args, Nothing)]-normalCon (RecC constr args) = [(constr, map (\(_,s,t) -> (s,t)) args, Nothing)]-normalCon (InfixC a constr b) = [(constr, [a,b], Nothing)]-normalCon (ForallC _ _ constr) = normalCon constr-normalCon (GadtC (constr:_) args typ) = [(constr,args,Just typ)]-normalCon (RecGadtC (constr : _) args typ) = [(constr,map dropFst args,Just typ)]- where dropFst (_,x,y) = (x,y)-normalCon _ = error "missing case for 'normalCon'"--normalCon' :: Con -> [(Name,[Type], Maybe Type)]-normalCon' con = map conv (normalCon con)- where conv (n, ts, t) = (n, map snd ts, t)- -mkInstanceD :: Cxt -> Type -> [Dec] -> Dec-mkInstanceD cxt ty decs = InstanceD Nothing cxt ty decs--{-|- This function returns the name of a bound type variable--}-tyVarBndrName (PlainTV n _) = n-tyVarBndrName (KindedTV n _ _) = n--{-|- This function abstracts away @newtype@ declaration, it turns them into- @data@ declarations.--}-abstractNewtype :: Info -> Maybe DataInfo-abstractNewtype (TyConI (NewtypeD cxt name args _ constr derive))- = Just (DataInfo cxt name args [constr] derive)-abstractNewtype (TyConI (DataD cxt name args _ constrs derive))- = Just (DataInfo cxt name args constrs derive)-abstractNewtype _ = Nothing--continuous :: Name -> Q [Dec]-continuous fname = do- Just (DataInfo _cxt name args constrs _deriving) <- abstractNewtypeQ $ reify fname- let argNames = map (VarT . tyVarBndrName) args- complType = foldl AppT (ConT name) argNames- preCond = map (mkClassP ''Continuous . (: [])) argNames- classType = AppT (ConT ''Continuous) complType- let constrs' = concatMap normalCon' constrs- progressAndNextDecl <- funD 'progressAndNext (map genProgressAndNext constrs')- progressInternalDecl <- funD 'progressInternal (map genProgressInternal constrs')- nextProgressDecl <- funD 'nextProgress (map genNextProgress constrs')- return [mkInstanceD preCond classType [progressAndNextDecl,progressInternalDecl,nextProgressDecl]]- where genProgressAndNext (constr, args,_) = do- let n = length args- varNs <- newNames n "x"- varNsR <- newNames n "y"- varNsS <- newNames n "z"- varIn <- newName "_inp"- let pat = ConP constr [] $ map VarP varNs-- progressInternalExp <- [|progressAndNext|]- let lets = zipWith3 (\ x y z -> ValD (TupP [VarP y, VarP z]) (NormalB (progressInternalExp `AppE` VarE varIn `AppE` VarE x)) []) varNs varNsR varNsS- clockUnionExp <- [|clockUnion|]- result <- appsE ( conE constr : (map varE varNsR))- clock <- if n == 0 then [|emptyClock|] else return (foldl1 (\ x y -> (clockUnionExp `AppE` x) `AppE` y) (map VarE varNsS))- let body = LetE lets (TupE [Just result, Just clock])- return $ Clause [VarP varIn, pat] (NormalB body) []- genProgressInternal (constr, args,_) = do- let n = length args- varNs <- newNames n "x"- varIn <- newName "_inp"- let pat = ConP constr [] $ map VarP varNs- allVars = map varE varNs- inpVar = varE varIn- body <- appsE ( conE constr : (map (\ x -> [|progressInternal $inpVar $x|]) allVars))- return $ Clause [VarP varIn, pat] (NormalB body) []- genNextProgress (constr, args,_) = do- let n = length args- varNs <- newNames n "x"- let pat = ConP constr [] $ map VarP varNs- allVars = map varE varNs- body <- if n == 0 then [|emptyClock|] else foldl1 (\ x y -> [|clockUnion $x $y|]) ((map (\ x -> [|nextProgress $x|]) allVars))- return $ Clause [pat] (NormalB body) []
− src/AsyncRattus/Future.hs
@@ -1,219 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TypeOperators #-}-{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}----- | Programming with futures.--module AsyncRattus.Future- ( F(..)- , SigF(..)- , mkSigF- , mkSigF'- , current- , future- , bindF- , mapF- , sync- , syncF- , switchAwait- , switch- , switchS- , filterMap- , filterMapAwait- , filterAwait- , filter- , trigger- , triggerAwait- , map- , mapAwait- , zipWith- , zipWithAwait- , fromSig- , scan- , scanAwait- )--where--import AsyncRattus-import AsyncRattus.Signal (Sig(..))-import Prelude hiding (map, filter, zipWith)-import AsyncRattus.Channels--newtype OneShot a = OneShot (F a)--instance Producer (OneShot a) a where- getCurrent (OneShot (Now x)) = Just' x- getCurrent (OneShot (Wait _)) = Nothing'-- getNext (OneShot (Now _)) cb = cb (never :: O (OneShot a))- getNext (OneShot (Wait x)) cb = cb (delay (OneShot (adv x)))--instance Producer p a => Producer (F p) a where- getCurrent (Now x) = getCurrent x- getCurrent (Wait _) = Nothing'- - getNext (Now x) cb = getNext x cb- getNext (Wait x) cb = cb x--instance Producer (SigF a) a where- getCurrent (x :>: _) = Just' x- getNext (_ :>: xs) cb = cb xs------ | @F a@ will produces a value of type @a@ after zero or more ticks--- of some clocks-data F a = Now !a | Wait !(O (F a))----bindF :: F a -> Box (a -> F b) -> F b-bindF (Now x) f = unbox f x-bindF (Wait x) f = Wait (delay (bindF (adv x) f))--mapF :: Box (a -> b) -> F a -> F b-mapF f d = d `bindF` (box (\ x -> Now (unbox f x)))---sync :: O (F a) -> O (F b) -> O (F a :* F b)-sync x y = delay (case select x y of- Fst x' y' -> (x' :* Wait y')- Snd x' y' -> (Wait x' :* y')- Both x' y' -> (x' :* y'))--syncF :: (Stable a, Stable b) => F a -> F b -> F (a :* b)-syncF (Now x) (Now y) = Now (x :* y)-syncF (Wait x) (Now y) = Wait (delay (syncA (adv x) y))-syncF (Now x) (Wait y) = Wait (delay (syncB x (adv y)))-syncF (Wait x) (Wait y) = Wait (delay (case select x y of- Fst x' y' -> syncF x' (Wait y')- Snd x' y' -> syncF (Wait x') y'- Both x' y' -> syncF x' y'- )) --syncA :: (Stable b) => F a -> b -> F (a :* b)-syncA (Now x) y = Now (x :* y)-syncA (Wait x) y = Wait (delay (syncA (adv x) y))---syncB :: (Stable a) => a -> F b -> F (a :* b)-syncB x (Now y) = Now (x :* y)-syncB x (Wait y) = Wait (delay (syncB x (adv y)))----- | @SigF a@ is a signal of values of type @a@. In contrast to 'Sig',--- 'SigF' supports the 'filter' and 'filterMap' functions.-data SigF a = !a :>: !(O (F (SigF a)))----- | Get the current value of a signal.-current :: SigF a -> a-current (x :>: _) = x----- | Get the future the signal.-future :: SigF a -> O (F (SigF a))-future (_ :>: xs) = xs---mkSigF :: Box (O a) -> F (SigF a)-mkSigF b = Wait (mkSigF' b) where--mkSigF' :: Box (O a) -> O (F (SigF a))-mkSigF' b = delay (Now (adv (unbox b) :>: mkSigF' b))---fromSig :: Sig a -> SigF a-fromSig (x ::: xs) = x :>: delay (Now (fromSig (adv xs)))-- -switchAwait :: F (SigF a) -> F (SigF a) -> F(SigF a)-switchAwait _ (Now ys) = Now ys-switchAwait (Now (x :>: xs)) (Wait ys) = Now (x :>: delay (uncurry' switchAwait (adv (sync xs ys)) ))-switchAwait (Wait xs) (Wait ys) = Wait (delay (uncurry' switchAwait (adv (sync xs ys)) ))--switch :: SigF a -> F (SigF a) -> SigF a-switch _ (Now ys) = ys-switch (x :>: xs) (Wait ys) = x :>: delay (uncurry' switchAwait (adv (sync xs ys)))--switchS :: Stable a => SigF a -> F (a -> SigF a) -> SigF a-switchS (x :>: _) (Now f) = f x-switchS (x :>: xs) (Wait ys) = x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys)))--switchAwaitS :: Stable a => a -> F (SigF a) -> F (a -> SigF a) -> F (SigF a)-switchAwaitS _ (Now (x :>: _)) (Now f) = Now (f x)-switchAwaitS _ (Now (x :>: xs)) (Wait ys) =- Now (x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))-switchAwaitS x (Wait _) (Now f) = Now (f x)-switchAwaitS x (Wait xs) (Wait ys) = Wait (delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))----filterMapAwait :: Box (a -> Maybe' b) -> F(SigF a) -> F (SigF b)-filterMapAwait f (Wait xs) = Wait (delay (filterMapAwait f (adv xs)))-filterMapAwait f (Now (x :>: xs)) = case unbox f x of- Just' y -> Now (y :>: delay (filterMapAwait f (adv xs)))- Nothing' -> Wait (delay (filterMapAwait f (adv xs)))--filterMap :: Box (a -> Maybe' b) -> SigF a -> F (SigF b)-filterMap f xs = filterMapAwait f (Now xs)---filterAwait :: Box (a -> Bool) -> F( SigF a) -> F (SigF a)-filterAwait p = filterMapAwait (box (\ x -> if unbox p x then Just' x else Nothing'))--filter :: Box (a -> Bool) -> SigF a -> F (SigF a)-filter p = filterMap (box (\ x -> if unbox p x then Just' x else Nothing'))--trigger :: Stable b => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c-trigger f (a :>: as) (b :>: bs) =- unbox f a b :>:- delay (uncurry' (trigger' b f) (adv (sync as bs)))--triggerAwait :: Stable b => Box (a -> b -> c) -> F (SigF a) -> SigF b -> F (SigF c)-triggerAwait f (Now (a :>: as)) (b :>: bs)- = Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))-triggerAwait f (Wait as) (b :>: bs)- = Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))--trigger' :: Stable b => b -> Box (a -> b -> c) -> F (SigF a) -> F (SigF b) -> F (SigF c)-trigger' b f (Now (a :>: as)) (Wait bs) =- Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))-trigger' _ f (Now (a :>: as)) (Now (b :>: bs)) =- Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))-trigger' b f (Wait as) (Wait bs) =- Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))-trigger' _ f (Wait as) (Now (b :>: bs)) =- Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))---mapAwait :: Box (a -> b) -> F (SigF a) -> F (SigF b)-mapAwait f (Now (x :>: xs)) = Now (unbox f x :>: delay (mapAwait f (adv xs)))-mapAwait f (Wait xs) = Wait (delay (mapAwait f (adv xs)))--map :: Box (a -> b) -> SigF a -> SigF b-map f (x :>: xs) = unbox f x :>: delay (mapAwait f (adv xs))----zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> SigF a -> SigF b -> SigF c-zipWith f (a :>: as) (b :>: bs) = unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs)))--zipWithAwait :: (Stable a, Stable b) => Box(a -> b -> c) -> a -> b -> F (SigF a) -> F (SigF b) -> F (SigF c)-zipWithAwait f _ _ (Now (a :>: as)) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))-zipWithAwait f _ b (Now (a :>: as)) (Wait bs) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))-zipWithAwait f a _ (Wait as) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))-zipWithAwait f a b (Wait as) (Wait bs) = Wait (delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))--scan :: (Stable b) => Box(b -> a -> b) -> b -> SigF a -> SigF b-scan f acc (a :>: as) = acc' :>: delay (scanAwait f acc' (adv as))- where acc' = unbox f acc a--scanAwait :: (Stable b) => Box (b -> a -> b) -> b -> F (SigF a) -> F (SigF b)-scanAwait f acc (Now (a :>: as)) = Now (acc' :>: delay (scanAwait f acc' (adv as)))- where acc' = unbox f acc a-scanAwait f acc (Wait as) = Wait (delay (scanAwait f acc (adv as)))
− src/AsyncRattus/InternalPrimitives.hs
@@ -1,263 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}--module AsyncRattus.InternalPrimitives where--import Prelude hiding (Left, Right)-import Data.IntSet (IntSet)-import qualified Data.IntSet as IntSet-import Data.IORef-import Control.Concurrent.MVar-import System.IO.Unsafe-import System.Mem.Weak-import Control.Monad---- An input channel is identified by an integer. The programmer should not know about it.-type InputChannelIdentifier = Int--type Clock = IntSet--singletonClock :: InputChannelIdentifier -> Clock-singletonClock = IntSet.singleton--emptyClock :: Clock-emptyClock = IntSet.empty--clockUnion :: Clock -> Clock -> Clock-clockUnion = IntSet.union--channelMember :: InputChannelIdentifier -> Clock -> Bool-channelMember = IntSet.member--data InputValue where- OneInput :: !InputChannelIdentifier -> !a -> InputValue- MoreInputs :: !InputChannelIdentifier -> !a -> !InputValue -> InputValue--inputInClock :: InputValue -> Clock -> Bool-inputInClock (OneInput ch _) cl = channelMember ch cl-inputInClock (MoreInputs ch _ more) cl = channelMember ch cl || inputInClock more cl----- | The "later" type modality. A value @v@ of type @O 𝜏@ consists of--- two components: Its clock, denoted @cl(v)@, and a delayed--- computation that will produce a value of type @𝜏@ as soon as the--- clock @cl(v)@ ticks. The clock @cl(v)@ is only used for type--- checking and is not directly accessible, whereas the delayed--- computation is accessible via 'adv' and 'select'.--data O a = Delay !Clock (InputValue -> a)---- | The return type of the 'select' primitive.-data Select a b = Fst !a !(O b) | Snd !(O a) !b | Both !a !b--asyncRattusError pr = error (pr ++ ": Did you forget to mark this as Async Rattus code?")---- | This is the constructor for the "later" modality 'O':------ > Γ ✓θ ⊢ t :: 𝜏--- > ----------------------- > Γ ⊢ delay t :: O 𝜏------ The typing rule requires that its argument @t@ typecheck with an--- additional tick @✓θ@ of some clock @θ@.-{-# INLINE [1] delay #-}-delay :: a -> O a-delay _ = asyncRattusError "delay"--extractClock :: O a -> Clock-extractClock (Delay cl _) = cl--{-# INLINE [1] adv' #-}-adv' :: O a -> InputValue -> a-adv' (Delay _ f) inp = f inp----- | This is the eliminator for the "later" modality 'O':------ > Γ ⊢ t :: O 𝜏 Γ' tick-free--- > ------------------------------------ > Γ ✓cl(t) Γ' ⊢ adv t :: 𝜏------ It requires that a tick @✓θ@ is in the context whose clock matches--- exactly the clock of @t@, i.e. @θ = cl(t)@.--{-# INLINE [1] adv #-}-adv :: O a -> a-adv _ = asyncRattusError "adv"---- | If we want to eliminate more than one delayed computation, i.e.\--- two @s :: O σ@ and @t :: O 𝜏@, we need to use 'select' instead of--- just 'adv'.------ > Γ ⊢ s :: O σ Γ ⊢ t :: O 𝜏 Γ' tick-free--- > ----------------------------------------------------- > Γ ✓cl(s)⊔cl(t) Γ' ⊢ select s t :: Select σ 𝜏------ It requires that we have a tick @✓θ@ in the context whose clock--- matches the union of the clocks of @s@ and @t@, i.e. @θ =--- cl(s)⊔cl(t)@. The union of two clocks ticks whenever either of the--- two clocks ticks, i.e. @cl(s)⊔cl(t)@, whenever @cl(s)@ or @cl(t)@--- ticks.------ That means there are three possible outcomes, which are reflected--- in the result type of @select s t@. A value of @Select σ 𝜏@ is--- either------ * a value of type @σ@ and a delayed computation of type @O 𝜏@, if--- @cl(s)@ ticks before @cl(t)@,------ * a value of type @𝜏@ and a delayed computation of type @O σ@, if--- @cl(t)@ ticks before @cl(s)@, or------ * a value of type @σ@ and a value of type @𝜏@, if @cl(s)@ and--- * @cl(s)@ tick simultaneously.---{-# INLINE [1] select #-}-select :: O a -> O b -> Select a b-select _ _ = asyncRattusError "select"--select' :: O a -> O b -> InputValue -> Select a b-select' a@(Delay clA inpFA) b@(Delay clB inpFB) inp- = if inputInClock inp clA then- if inputInClock inp clB then Both (inpFA inp) (inpFB inp)- else Fst (inpFA inp) b- else Snd a (inpFB inp)------ | The clock of @never :: O 𝜏@ will never tick, i.e. it will never--- produce a value of type @𝜏@. With 'never' we can for example--- implement the constant signal @x ::: never@ of type @Sig a@ for any @x ::--- a@.-never :: O a-never = Delay emptyClock (error "Trying to adv on the 'never' delayed computation")---- | A type is @Stable@ if it is a strict type and the later modality--- @O@ and function types only occur under @Box@.------ For example, these types are stable: @Int@, @Box (a -> b)@, @Box (O--- Int)@, @Box (Sig a -> Sig b)@.------ But these types are not stable: @[Int]@ (because the list type is--- not strict), @Int -> Int@, (function type is not stable), @O--- Int@, @Sig Int@.--class Stable a where------ | The "stable" type modality. A value of type @Box a@ is a--- time-independent computation that produces a value of type @a@.--- Use 'box' and 'unbox' to construct and consume 'Box'-types.-data Box a = Box a----- | This is the constructor for the "stable" modality 'Box':------ > Γ☐ ⊢ t :: 𝜏--- > ----------------------- > Γ ⊢ box t :: Box 𝜏------ where Γ☐ is obtained from Γ by removing all ticks and all variables--- @x :: 𝜏@, where 𝜏 is not a stable type.--{-# INLINE [1] box #-}-box :: a -> Box a-box x = Box x----- | This is the eliminator for the "stable" modality 'Box':------ > Γ ⊢ t :: Box 𝜏--- > --------------------- > Γ ⊢ unbox t :: 𝜏-{-# INLINE [1] unbox #-}-unbox :: Box a -> a-unbox (Box d) = d---defaultPromote :: Continuous a => a -> Box a-defaultPromote x = unsafePerformIO $ - do r <- newIORef x- r' <- mkWeakIORef r (return ()) - modifyIORef promoteStore (ContinuousData r' :)- return (Box (unsafePerformIO $ readIORef r))---class Continuous p where- -- | Computes the same as 'progressInternal' and 'nextProgress'. In- -- particular @progressAndNext inp v = (progressInternal inp v,- -- nextProgress (progressInternal inp v))@.- progressAndNext :: InputValue -> p -> (p , Clock)-- -- | Progresses the continuous value, given the input value from- -- some channel- progressInternal :: InputValue -> p -> p- -- | Computes the set of channels that the continuous value is- -- depending on. That is if @nextProgress v = cl@ and a new input- -- @inp@ on channel @ch@ arrives, then @progressInternal inp v = v@.- nextProgress :: p -> Clock - promoteInternal :: p -> Box p- promoteInternal = defaultPromote---- For stable types we can circumvent the "promote store".-instance {-# OVERLAPPABLE #-} Stable a => Continuous a where- progressAndNext _ x = (x , emptyClock) - progressInternal _ x = x- nextProgress _ = emptyClock- promoteInternal = Box--data ContinuousData where- ContinuousData :: Continuous a => !(Weak (IORef a)) -> ContinuousData---- TODO: The list type needs to be replaced by a more efficient--- mutable data structure.-{-# NOINLINE promoteStore #-}-promoteStore :: IORef [ContinuousData]-promoteStore = unsafePerformIO (newIORef [])--{-# NOINLINE progressPromoteStoreMutex #-}-progressPromoteStoreMutex :: MVar ()-progressPromoteStoreMutex = unsafePerformIO (newMVar ())----- | Atomic version of 'progressPromoteStore'.--progressPromoteStoreAtomic :: InputValue -> IO ()-progressPromoteStoreAtomic inp = do- takeMVar progressPromoteStoreMutex- progressPromoteStore inp- putMVar progressPromoteStoreMutex ()----- | For promote to work, its argument must be stored in the "promote--- store", and whenenver an input is received on some channel, all--- values in the "promote store" must be advanced (using--- 'progressInternal').--progressPromoteStore :: InputValue -> IO ()-progressPromoteStore inp = do - xs <- atomicModifyIORef promoteStore (\x -> ([],x))- xs' <- filterM run xs- atomicModifyIORef promoteStore (\x -> (x ++ xs',()))- where run (ContinuousData x) = do- d <- deRefWeak x- case d of- Nothing -> return False- Just x -> modifyIORef' x (progressInternal inp) >> return True--promote :: Continuous a => a -> Box a-promote x = promoteInternal x--newtype Chan a = Chan InputChannelIdentifier--{-# RULES- "unbox/box" forall x. unbox (box x) = x- #-}---{-# RULES- "box/unbox" forall x. box (unbox x) = x- #-}
− src/AsyncRattus/Plugin.hs
@@ -1,162 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE CPP #-}----- | The plugin to make it all work.--module AsyncRattus.Plugin (plugin, AsyncRattus(..)) where-import AsyncRattus.Plugin.StableSolver-import AsyncRattus.Plugin.ScopeCheck-import AsyncRattus.Plugin.Strictify-import AsyncRattus.Plugin.SingleTick-import AsyncRattus.Plugin.CheckClockCompatibility-import AsyncRattus.Plugin.Utils-import AsyncRattus.Plugin.Annotation-import AsyncRattus.Plugin.Transform--import Prelude hiding ((<>))--import Control.Monad-import Data.Maybe-import Data.List-import Data.Data hiding (tyConName)-import qualified Data.Set as Set-import Data.Set (Set)--import qualified GHC.LanguageExtensions as LangExt--import GHC.Plugins-import GHC.Tc.Types---- | Use this to enable Asynchronous Rattus' plugin, either by supplying the option--- @-fplugin=AsyncRattus.Plugin@ directly to GHC, or by including the--- following pragma in each source file:--- --- > {-# OPTIONS -fplugin=AsyncRattus.Plugin #-}-plugin :: Plugin-plugin = defaultPlugin {- installCoreToDos = install,- pluginRecompile = purePlugin,- typeCheckResultAction = typechecked,- tcPlugin = tcStable,- driverPlugin = updateEnv- }---data Options = Options {debugMode :: Bool}----- | Enable the @Strict@ language extension.-updateEnv :: [CommandLineOption] -> HscEnv -> IO HscEnv-updateEnv _ env = return env {hsc_dflags = update (hsc_dflags env) } - where update fls = xopt_set fls LangExt.Strict--typechecked :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv-typechecked _ _ env = checkAll env >> return env--install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]-install opts todo = case find findSamePass todo of -- check that we don't run the transformation twice- Nothing -> return (strPass : todo) -- (e.g. if the "-fplugin" option is used twice)- _ -> return todo- where name = "Async Rattus strictify"- strPass = CoreDoPluginPass name (strictifyProgram Options{debugMode = dmode})- dmode = "debug" `elem` opts- findSamePass (CoreDoPluginPass s _) = s == name- findSamePass _ = False- ---- | Apply the following operations to all Asynchronous Rattus definitions in the--- program:------ * Transform into single tick form (see SingleTick module)--- * Check whether lazy data types are used (see Strictify module)--- * Transform into call-by-value form (see Strictify module)--strictifyProgram :: Options -> ModGuts -> CoreM ModGuts-strictifyProgram opts guts = do- newBinds <- mapM (strictify opts guts) (mg_binds guts)- return guts { mg_binds = newBinds }--strictify :: Options -> ModGuts -> CoreBind -> CoreM CoreBind-strictify opts guts b@(Rec bs) = do- let debug = debugMode opts- tr <- liftM or (mapM (shouldProcessCore guts . fst) bs)- if tr then do- let vs = map fst bs- es' <- mapM (\ (v,e) -> do- processCore <- shouldProcessCore guts v- if not processCore- then do- when debug $ putMsg $ text "Skipping binding: " <> ppr v- return e- else checkAndTransform guts (Set.fromList vs) debug v e- ) bs- when debug $ putMsg $ "Plugin | result of transformation: " <> ppr es'- return (Rec (zip vs es'))- else return b-strictify opts guts b@(NonRec v e) = do- let debug = debugMode opts- when debug $ putMsg $ text "Processing binding: " <> ppr v <> text " | Non-recursive binding"- when debug $ putMsg $ text "Expr: " <> ppr e- processCore <- shouldProcessCore guts v- if not processCore then do- when debug $ putMsg $ text "Skipping binding: " <> ppr v- return b- else do- transformed <- checkAndTransform guts Set.empty debug v e- when debug $ putMsg $ "Plugin | result of transformation: " <> ppr transformed- return $ NonRec v transformed--checkAndTransform :: ModGuts -> Set Var -> Bool -> Var -> CoreExpr -> CoreM CoreExpr-checkAndTransform guts recursiveSet debug v e = do- when debug $ putMsg $ text "Processing binding: " <> ppr v- when debug $ putMsg $ text "Expr: " <> ppr e- allowRec <- allowRecursion guts v- singleTick <- toSingleTick e- when debug $ putMsg $ text "Single-tick: " <> ppr singleTick- lazy <- allowLazyData guts v- when (not lazy) $ checkStrictData (SCxt (nameSrcSpan $ getName v)) singleTick- when debug $ putMsg $ text "Strict single-tick: " <> ppr singleTick- checkExpr CheckExpr{ recursiveSet = recursiveSet, oldExpr = e,- verbose = debug,- allowRecExp = allowRec} singleTick- transform singleTick--getModuleAnnotations :: Data a => ModGuts -> [a]-getModuleAnnotations guts = anns'- where anns = filter (\a-> case ann_target a of- ModuleTarget m -> m == (mg_module guts)- _ -> False) (mg_anns guts)- anns' = mapMaybe (fromSerialized deserializeWithData . ann_value) anns-----allowLazyData :: ModGuts -> CoreBndr -> CoreM Bool-allowLazyData guts bndr = do- l <- annotationsOn guts bndr :: CoreM [AsyncRattus]- return (AllowLazyData `elem` l)--allowRecursion :: ModGuts -> CoreBndr -> CoreM Bool-allowRecursion guts bndr = do- l <- annotationsOn guts bndr :: CoreM [AsyncRattus]- return (AllowRecursion `elem` l)--expectError :: ModGuts -> CoreBndr -> CoreM Bool-expectError guts bndr = do- l <- annotationsOn guts bndr :: CoreM [InternalAnn]- return $ ExpectError `elem` l---shouldProcessCore :: ModGuts -> CoreBndr -> CoreM Bool-shouldProcessCore guts bndr = do- expectScopeError <- expectError guts bndr- return (userFunction bndr && not expectScopeError)--annotationsOn :: (Data a) => ModGuts -> CoreBndr -> CoreM [a]-annotationsOn guts bndr = do- (_,anns) <- getAnnotations deserializeWithData guts- return $- lookupWithDefaultUFM anns [] (varName bndr) ++- getModuleAnnotations guts
− src/AsyncRattus/Plugin/Annotation.hs
@@ -1,30 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-module AsyncRattus.Plugin.Annotation (AsyncRattus(..), InternalAnn (..)) where--import Data.Data---- | By default all Async Rattus functions are checked for use of lazy--- data types, since these may cause memory leaks. If any lazy data--- types are used, a warning is issued. These warnings can be disabled--- by annotating the module or the function with 'AllowLazyData'------ > {-# ANN myFunction AllowLazyData #-}--- >--- > {-# ANN module AllowLazyData #-}------ Async Rattus only allows guarded recursion, i.e. recursive calls--- must occur in the scope of a tick. Structural recursion over strict--- data types is safe as well, but is currently not checked. To--- disable the guarded recursion check, annotate the module or--- function with 'AllowRecursion'.--- --- > {-# ANN myFunction AllowRecursion #-}--- >--- > {-# ANN module AllowRecursion #-}---data AsyncRattus = AllowLazyData | AllowRecursion deriving (Typeable, Data, Show, Ord, Eq)----- | This annotation type is for internal use only.-data InternalAnn = ExpectError | ExpectWarning deriving (Typeable, Data, Show, Eq, Ord)
− src/AsyncRattus/Plugin/CheckClockCompatibility.hs
@@ -1,261 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE CPP #-}---- | This module implements the check that the transformed code is--- typable in the single tick calculus.--module AsyncRattus.Plugin.CheckClockCompatibility- (checkExpr, CheckExpr (..)) where--import GHC.Types.Tickish-import GHC.Plugins--import AsyncRattus.Plugin.Utils-import qualified AsyncRattus.Plugin.PrimExpr as Prim-import Prelude hiding ((<>))-import Data.Set (Set)-import qualified Data.Set as Set-import Data.Map (Map)-import qualified Data.Map as Map-import Data.Maybe (isJust)-import Control.Monad (foldM, when)-import Control.Applicative ((<|>))-import System.Exit (exitFailure)--type LCtx = Set Var-data HiddenReason = BoxApp | AdvApp | NestedRec Var | FunDef | DelayApp-type Hidden = Map Var HiddenReason--data TypeError = TypeError SrcSpan SDoc---data Ctx = Ctx- { current :: LCtx,- hidden :: Hidden,- earlier :: Maybe LCtx,- srcLoc :: SrcSpan,- recDef :: Set Var, -- ^ recursively defined variables - stableTypes :: Set Var,- allowRecursion :: Bool,- allowGuardedRec :: Bool- }--hasTick :: Ctx -> Bool-hasTick = isJust . earlier--stabilize :: HiddenReason -> Ctx -> Ctx-stabilize hr c = c- {current = Set.empty,- earlier = Nothing,- hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid,- allowGuardedRec = False- }- where ctxHid = maybe (current c) (Set.union (current c)) (earlier c)--data Scope = Hidden SDoc | Visible--getScope :: Ctx -> Var -> Scope-getScope c v =- if v `Set.member` recDef c then- if allowGuardedRec c || allowRecursion c || typeClassFunction v then Visible- else Hidden ("(Mutually) recursive call to " <> ppr v <> " must occur under delay")- else case Map.lookup v (hidden c) of- Just reason ->- if (isStable (stableTypes c) (varType v)) then Visible- else case reason of- NestedRec rv ->- if allowRecursion c then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope:"- $$ "It appears in a local recursive definition (namely of " <> ppr rv <> ")"- $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- BoxApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$- "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")-- FunDef -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")- DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under two occurrences of delay and is a of a non-stable type " <> ppr (varType v))- Nothing- | maybe False (Set.member v) (earlier c) ->- if isStable (stableTypes c) (varType v) then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$- "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- | Set.member v (current c) -> Visible- | otherwise -> Visible----pickFirst :: SrcSpan -> SrcSpan -> SrcSpan-pickFirst s@RealSrcSpan{} _ = s-pickFirst _ s = s--typeError :: Ctx -> Var -> SDoc -> TypeError-typeError ctx var = TypeError (pickFirst (srcLoc ctx) (nameSrcSpan (varName var)))--instance Outputable TypeError where- ppr (TypeError srcLoc sdoc) = text "TypeError at " <> ppr srcLoc <> text ": " <> ppr sdoc--emptyCtx :: CheckExpr -> Ctx-emptyCtx c =- Ctx { current = Set.empty,- earlier = Nothing,- hidden = Map.empty,- srcLoc = noLocationInfo,- recDef = recursiveSet c,- stableTypes = Set.empty,- allowRecursion = allowRecExp c,- allowGuardedRec = False- }--stabilizeLater :: Ctx -> Ctx-stabilizeLater c =- case earlier c of- Just earl -> c {earlier = Nothing,- hidden = hidden c `Map.union` Map.fromSet (const FunDef) earl}- Nothing -> c--isStableConstr :: Type -> CoreM (Maybe Var)-isStableConstr t =- case splitTyConApp_maybe t of- Just (con,[args]) ->- case getNameModule con of- Just (name, mod) ->- if isRattModule mod && name == "Stable"- then return (getTyVar_maybe args)- else return Nothing- _ -> return Nothing- _ -> return Nothing---- should be equatable-type SymbolicClock = Set Var--mkClock1 :: Var -> SymbolicClock-mkClock1 = Set.singleton--mkClock2 :: Var -> Var -> SymbolicClock-mkClock2 v1 v2 = Set.fromList [v1, v2]--newtype CheckResult = CheckResult{- -- if present, contains the variable of the primitive applied so we can report its position- -- in case of an error, and the clock for the primitive- prim :: Maybe (Var, SymbolicClock)-}--instance Outputable CheckResult where- ppr (CheckResult prim) = text "CheckResult {prim = " <> ppr prim <> text "}"--emptyCheckResult :: CheckResult-emptyCheckResult = CheckResult {prim = Nothing}--data CheckExpr = CheckExpr{- recursiveSet :: Set Var,- oldExpr :: Expr Var,- verbose :: Bool,- allowRecExp :: Bool- }--checkExpr :: CheckExpr -> Expr Var -> CoreM ()-checkExpr c e = do- when (verbose c) $ putMsg $ text "checkExpr: " <> ppr e- res <- checkExpr' (emptyCtx c) e- case res of- Right _ -> do when (verbose c) $ putMsgS "checkExpr succeeded."- Left (TypeError src doc) ->- let printErrMsg = if verbose c- then do- printMessage SevError src ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$ doc)- putMsgS "-------- old --------"- putMsg $ ppr (oldExpr c)- putMsgS "-------- new --------"- putMsg (ppr e)- - else do- printMessage SevError noSrcSpan ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$- "Compile with flags \"-fplugin-opt AsyncRattus.Plugin:debug\" and \"-g2\" for detailed information")- in do- printErrMsg- liftIO exitFailure---checkExpr' :: Ctx -> Expr Var -> CoreM (Either TypeError CheckResult)-checkExpr' c (App e e') | isType e' || (not $ tcIsLiftedTypeKind $ typeKind $ exprType e')- = checkExpr' c e-checkExpr' c@Ctx{current = cur, earlier = earl} expr@(App e e') =- case Prim.isPrimExpr expr of- Just (Prim.BoxApp _) ->- checkExpr' (stabilize BoxApp c) e'- Just (Prim.DelayApp f _) -> do- let c' = case earl of- Nothing -> c{current = Set.empty, earlier = Just cur, allowGuardedRec = True}- Just earl' -> c{ current = Set.empty, earlier = Just cur, allowGuardedRec = True,- hidden = hidden c `Map.union` Map.fromSet (const DelayApp) earl'}- eRes <- checkExpr' c' e'- case eRes of- Left err -> return $ Left err- Right (CheckResult {prim = Nothing}) -> return $ Left $ typeError c f (text "Each delay must contain an adv or select")- Right _ -> return $ Right emptyCheckResult- Just (Prim.AdvApp f _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use adv under delay")- Just (Prim.AdvApp f (arg, _)) -> return $ Right $ CheckResult {prim = Just (f, mkClock1 arg)}- Just (Prim.SelectApp f _ _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use select under delay")- Just (Prim.SelectApp f (arg1, _) (arg2, _))-> return $ Right $ CheckResult {prim = Just (f, mkClock2 arg1 arg2)}- Nothing -> checkBoth c e e'-checkExpr' c (Case e v _ alts) = do- res <- checkExpr' c' e- resAll <- mapM (\(Alt _ _ altE) -> checkExpr' c altE) alts- foldM (fmap return . combine c) res resAll- where c' = addVars [v] c-checkExpr' c (Lam v e)- | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = do- is <- isStableConstr (varType v)- let c' = case is of- Nothing -> c- Just t -> c{stableTypes = Set.insert t (stableTypes c)}- checkExpr' c' e- | otherwise = checkExpr' (addVars [v] (stabilizeLater c)) e-checkExpr' _ (Type _) = return $ Right emptyCheckResult-checkExpr' _ (Lit _) = return $ Right emptyCheckResult-checkExpr' _ (Coercion _) = return $ Right emptyCheckResult-checkExpr' c (Tick (SourceNote span _name) e) =- checkExpr' c{srcLoc = fromRealSrcSpan span} e-checkExpr' c (Tick _ e) = checkExpr' c e-checkExpr' c (Cast e _) = checkExpr' c e-checkExpr' c (Let (NonRec _ e1) e2) = do- res1 <- checkExpr' c e1- res2 <- checkExpr' c e2- return $ combine c res1 res2-checkExpr' c (Let (Rec binds) e2) = do- resAll <- mapM (\ (v,e) -> checkExpr' (c' v) e) binds- res <- checkExpr' (addVars vs c) e2- foldM (fmap return . combine c) res resAll- where vs = map fst binds- ctxHid = maybe (current c) (Set.union (current c)) (earlier c)- c' v = c {current = Set.empty,- earlier = Nothing,- hidden = hidden c `Map.union`- Map.fromSet (const (NestedRec v)) ctxHid,- recDef = recDef c `Set.union` Set.fromList vs }-checkExpr' c (Var v)- | tcIsLiftedTypeKind $ typeKind $ varType v = case getScope c v of- Hidden reason -> return $ Left $ typeError c v reason- Visible -> return $ Right emptyCheckResult- | otherwise = return $ Right emptyCheckResult--addVars :: [Var] -> Ctx -> Ctx-addVars v c = c{current = Set.fromList v `Set.union` current c }--checkBoth :: Ctx -> CoreExpr -> CoreExpr -> CoreM (Either TypeError CheckResult)-checkBoth c e e' = do- c1 <- checkExpr' c e- c2 <- checkExpr' c e'- return $ combine c c1 c2---- Combines two CheckResults such that the clocks therein are compatible.--- If both CheckResults have PrimVars, one is picked arbitrarily.-combine :: Ctx -> Either TypeError CheckResult -> Either TypeError CheckResult -> Either TypeError CheckResult-combine c eRes1 eRes2 = do- res1 <- eRes1- res2 <- eRes2- case (res1, res2) of- (CheckResult (Just (_, cl1)), CheckResult (Just (_, cl2))) | cl1 == cl2 -> Right res2- (CheckResult (Just _), CheckResult (Just (p, _))) -> Left $ typeError c p "Only one adv/select allowed in a delay"- (CheckResult maybeP, CheckResult maybeP') -> Right $ CheckResult {prim = maybeP <|> maybeP'}
− src/AsyncRattus/Plugin/Dependency.hs
@@ -1,385 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE GADTs #-}---- | This module is used to perform a dependency analysis of top-level--- function definitions, i.e. to find out which defintions are--- (mutual) recursive. To this end, this module also provides--- functions to compute, bound variables and variable occurrences.--module AsyncRattus.Plugin.Dependency (dependency, HasBV (..),printBinds) where---import GHC.Plugins-import GHC.Data.Bag-import GHC.Hs.Type---import GHC.Hs.Extension-import GHC.Hs.Expr-import GHC.Hs.Pat-import GHC.Hs.Binds--#if __GLASGOW_HASKELL__ >= 904-import GHC.Parser.Annotation-#else-import Language.Haskell.Syntax.Extension-import GHC.Parser.Annotation-#endif---import Data.Set (Set)-import qualified Data.Set as Set-import Data.Graph-import Data.Maybe-import Data.Either-import Prelude hiding ((<>))------ | Compute the dependencies of a bag of bindings, returning a list--- of the strongly-connected components.-dependency :: Bag (LHsBindLR GhcTc GhcTc) -> [SCC (LHsBindLR GhcTc GhcTc, Set Var)]-dependency binds = map AcyclicSCC noDeps ++ catMaybes (map filterJust (stronglyConnComp (concat deps)))- where (deps,noDeps) = partitionEithers $ map mkDep $ bagToList binds- mkDep :: GenLocated l (HsBindLR GhcTc GhcTc) ->- Either [(Maybe (GenLocated l (HsBindLR GhcTc GhcTc), Set Var), Name, [Name])]- (GenLocated l (HsBindLR GhcTc GhcTc), Set Var)- mkDep b =- let dep = map varName $ Set.toList (getFV b)- vars = getBV b in- case Set.toList vars of- (v:vs) -> Left ((Just (b,vars), varName v , dep) : map (\ v' -> (Nothing, varName v' , dep)) vs)- [] -> Right (b,vars)- filterJust (AcyclicSCC Nothing) = Nothing -- this should not happen- filterJust (AcyclicSCC (Just b)) = Just (AcyclicSCC b)- filterJust (CyclicSCC bs) = Just (CyclicSCC (catMaybes bs))---printBinds (AcyclicSCC bind) = liftIO (putStr "acyclic bind: ") >> printBind (fst bind) >> liftIO (putStrLn "") -printBinds (CyclicSCC binds) = liftIO (putStr "cyclic binds: ") >> mapM_ (printBind . fst) binds >> liftIO (putStrLn "") ---printBind (L _ FunBind{fun_id = L _ name}) = - liftIO $ putStr $ (getOccString name ++ " ")-printBind (L _ (VarBind {var_id = name})) = liftIO $ putStr $ (getOccString name ++ " ")-#if __GLASGOW_HASKELL__ < 904-printBind (L _ (AbsBinds {abs_exports = exp})) = -#else-printBind (L _ (XHsBindsLR (AbsBinds {abs_exports = exp}))) = -#endif- mapM_ (\ e -> liftIO $ putStr $ ((getOccString $ abe_poly e) ++ " ")) exp-printBind _ = return ()----- | Computes the variables that are bound by a given piece of syntax.--class HasBV a where- getBV :: a -> Set Var--instance HasBV (HsBindLR GhcTc GhcTc) where- getBV (FunBind{fun_id = L _ v}) = Set.singleton v- getBV (PatBind {pat_lhs = pat}) = getBV pat- getBV (VarBind {var_id = v}) = Set.singleton v- getBV PatSynBind{} = Set.empty-#if __GLASGOW_HASKELL__ < 904- getBV (AbsBinds {abs_exports = es}) = Set.fromList (map abe_poly es)-#else- getBV (XHsBindsLR (AbsBinds {abs_exports = es})) = Set.fromList (map abe_poly es)-#endif- -instance HasBV a => HasBV (GenLocated b a) where- getBV (L _ e) = getBV e--instance HasBV a => HasBV [a] where- getBV ps = foldl (\s p -> getBV p `Set.union` s) Set.empty ps--#if __GLASGOW_HASKELL__ >= 904-getRecFieldRhs = hfbRHS-#else-getRecFieldRhs = hsRecFieldArg-#endif--getConBV (PrefixCon _ ps) = getBV ps-getConBV (InfixCon p p') = getBV p `Set.union` getBV p'-getConBV (RecCon (HsRecFields {rec_flds = fs})) = foldl run Set.empty fs- where run s (L _ f) = getBV (getRecFieldRhs f) `Set.union` s--#if __GLASGOW_HASKELL__ < 904-instance HasBV CoPat where- getBV CoPat {co_pat_inner = p} = getBV p-#else-instance HasBV XXPatGhcTc where- getBV CoPat {co_pat_inner = p} = getBV p- getBV (ExpansionPat _ p) = getBV p-#endif--instance HasBV (Pat GhcTc) where- getBV (VarPat _ (L _ v)) = Set.singleton v- getBV (LazyPat _ p) = getBV p-#if __GLASGOW_HASKELL__ >= 906- getBV (AsPat _ (L _ v) _ p) = Set.insert v (getBV p)-#else- getBV (AsPat _ (L _ v) p) = Set.insert v (getBV p)-#endif- getBV (BangPat _ p) = getBV p- getBV (ListPat _ ps) = getBV ps- getBV (TuplePat _ ps _) = getBV ps- getBV (SumPat _ p _ _) = getBV p- getBV (ViewPat _ _ p) = getBV p-- getBV (SplicePat _ sp) =- case sp of-#if __GLASGOW_HASKELL__ < 906- HsTypedSplice _ _ v _ -> Set.singleton v- HsSpliced _ _ (HsSplicedPat p) -> getBV p- HsUntypedSplice _ _ v _ -> Set.singleton v- HsQuasiQuote _ p p' _ _ -> Set.fromList [p,p']- _ -> Set.empty-#else- HsUntypedSpliceExpr _ e -> getFV e- HsQuasiQuote _ v _ -> Set.singleton v-#endif-- getBV (NPlusKPat _ (L _ v) _ _ _ _) = Set.singleton v- getBV (NPat {}) = Set.empty- getBV (XPat p) = getBV p- getBV (WildPat {}) = Set.empty- getBV (LitPat {}) = Set.empty-#if __GLASGOW_HASKELL__ >= 904 - getBV (ParPat _ _ p _) = getBV p-#else- getBV (ParPat _ p) = getBV p-#endif- getBV (ConPat {pat_args = con}) = getConBV con- getBV (SigPat _ p _) = getBV p--#if __GLASGOW_HASKELL__ < 904-instance HasBV NoExtCon where- getBV _ = Set.empty-#endif---- | Syntax that may contain variables.-class HasFV a where- -- | Compute the set of variables occurring in the given piece of- -- syntax. The name falsely suggests that returns free variables,- -- but in fact it returns all variable occurrences, no matter- -- whether they are free or bound.- getFV :: a -> Set Var --instance HasFV a => HasFV (GenLocated b a) where- getFV (L _ e) = getFV e- -instance HasFV a => HasFV [a] where- getFV es = foldMap getFV es--instance HasFV a => HasFV (Bag a) where- getFV es = foldMap getFV es--instance HasFV Var where- getFV v = Set.singleton v--instance HasFV a => HasFV (MatchGroup GhcTc a) where- getFV MG {mg_alts = alts} = getFV alts- -instance HasFV a => HasFV (Match GhcTc a) where- getFV Match {m_grhss = rhss} = getFV rhss--instance HasFV (HsTupArg GhcTc) where- getFV (Present _ e) = getFV e- getFV Missing {} = Set.empty--instance HasFV a => HasFV (GRHS GhcTc a) where- getFV (GRHS _ g b) = getFV g `Set.union` getFV b--instance HasFV a => HasFV (GRHSs GhcTc a) where- getFV GRHSs {grhssGRHSs = rhs, grhssLocalBinds = lbs} =- getFV rhs `Set.union` getFV lbs---instance HasFV (HsLocalBindsLR GhcTc GhcTc) where- getFV (HsValBinds _ bs) = getFV bs- getFV (HsIPBinds _ bs) = getFV bs- getFV EmptyLocalBinds {} = Set.empty- -instance HasFV (HsValBindsLR GhcTc GhcTc) where- getFV (ValBinds _ b _) = getFV b- getFV (XValBindsLR b) = getFV b--instance HasFV (NHsValBindsLR GhcTc) where- getFV (NValBinds bs _) = foldMap (getFV . snd) bs--instance HasFV (HsBindLR GhcTc GhcTc) where- getFV FunBind {fun_matches = ms} = getFV ms- getFV PatBind {pat_rhs = rhs} = getFV rhs- getFV VarBind {var_rhs = rhs} = getFV rhs- getFV PatSynBind {} = Set.empty-#if __GLASGOW_HASKELL__ < 904- getFV AbsBinds {abs_binds = bs} = getFV bs-#else- getFV (XHsBindsLR AbsBinds {abs_binds = bs}) = getFV bs-#endif--instance HasFV (IPBind GhcTc) where- getFV (IPBind _ _ e) = getFV e--instance HasFV (HsIPBinds GhcTc) where- getFV (IPBinds _ bs) = getFV bs- -instance HasFV (ApplicativeArg GhcTc) where- getFV ApplicativeArgOne { arg_expr = e } = getFV e- getFV ApplicativeArgMany {app_stmts = es, final_expr = e} = getFV es `Set.union` getFV e--instance HasFV (ParStmtBlock GhcTc GhcTc) where- getFV (ParStmtBlock _ es _ _) = getFV es- -instance HasFV a => HasFV (StmtLR GhcTc GhcTc a) where- getFV (LastStmt _ e _ _) = getFV e- getFV (BindStmt _ _ e) = getFV e- getFV (ApplicativeStmt _ args _) = foldMap (getFV . snd) args- getFV (BodyStmt _ e _ _) = getFV e- getFV (LetStmt _ bs) = getFV bs- getFV (ParStmt _ stms e _) = getFV stms `Set.union` getFV e- getFV TransStmt{} = Set.empty -- TODO- getFV RecStmt{} = Set.empty -- TODO---instance HasFV (HsRecFields GhcTc (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where- getFV HsRecFields{rec_flds = fs} = getFV fs--#if __GLASGOW_HASKELL__ >= 904-instance HasFV (HsFieldBind o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where-#else-instance HasFV (HsRecField' o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where-#endif- getFV rf = getFV (getRecFieldRhs rf)--instance HasFV (ArithSeqInfo GhcTc) where- getFV (From e) = getFV e- getFV (FromThen e1 e2) = getFV e1 `Set.union` getFV e2- getFV (FromTo e1 e2) = getFV e1 `Set.union` getFV e2- getFV (FromThenTo e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3- -#if __GLASGOW_HASKELL__ >= 904-instance HasFV (HsQuote GhcTc) where-#else-instance HasFV (HsBracket GhcTc) where-#endif- getFV (ExpBr _ e) = getFV e- getFV (VarBr _ _ e) = getFV e- getFV _ = Set.empty--instance HasFV (HsCmd GhcTc) where- getFV (HsCmdArrApp _ e1 e2 _ _) = getFV e1 `Set.union` getFV e2- getFV (HsCmdArrForm _ e _ _ cmd) = getFV e `Set.union` getFV cmd- getFV (HsCmdApp _ e1 e2) = getFV e1 `Set.union` getFV e2- getFV (HsCmdLam _ l) = getFV l- getFV (HsCmdCase _ _ mg) = getFV mg- getFV (HsCmdIf _ _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3- getFV (HsCmdDo _ cmd) = getFV cmd-#if __GLASGOW_HASKELL__ >= 904- getFV (HsCmdPar _ _ cmd _) = getFV cmd- getFV (HsCmdLet _ _ bs _ _) = getFV bs-#else- getFV (HsCmdPar _ cmd) = getFV cmd- getFV (HsCmdLet _ bs _) = getFV bs-#endif-#if __GLASGOW_HASKELL__ >= 904- getFV (HsCmdLamCase _ _ mg) = getFV mg-#else- getFV (HsCmdLamCase _ mg) = getFV mg-#endif- getFV (XCmd e) = getFV e---instance (HasFV a, HasFV b) => HasFV (Either a b) where- getFV (Left x) = getFV x- getFV (Right x) = getFV x--#if __GLASGOW_HASKELL__ >= 908-instance HasFV (LHsRecUpdFields GhcTc) where- getFV RegularRecUpdFields {recUpdFields = x} = getFV x- getFV OverloadedRecUpdFields {olRecUpdFields = x} = getFV x-#endif--instance HasFV (HsCmdTop GhcTc) where- getFV (HsCmdTop _ cmd) = getFV cmd--instance HasFV (HsExpr GhcTc) where- getFV (HsVar _ v) = getFV v- getFV HsUnboundVar {} = Set.empty- getFV HsOverLabel {} = Set.empty- getFV HsIPVar {} = Set.empty- getFV HsOverLit {} = Set.empty- getFV HsLit {} = Set.empty- getFV (HsLam _ mg) = getFV mg- getFV (HsApp _ e1 e2) = getFV e1 `Set.union` getFV e2 - getFV (OpApp _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3- getFV (NegApp _ e _) = getFV e- getFV (SectionL _ e1 e2) = getFV e1 `Set.union` getFV e2- getFV (SectionR _ e1 e2) = getFV e1 `Set.union` getFV e2- getFV (ExplicitTuple _ es _) = getFV es- getFV (ExplicitSum _ _ _ e) = getFV e- getFV (HsCase _ e mg) = getFV e `Set.union` getFV mg- getFV (HsMultiIf _ es) = getFV es- getFV (HsDo _ _ e) = getFV e- getFV HsProjection {} = Set.empty- getFV HsGetField {gf_expr = e} = getFV e- getFV (ExplicitList _ es) = getFV es- getFV (RecordUpd {rupd_expr = e, rupd_flds = fs}) = getFV e `Set.union` getFV fs- getFV (RecordCon {rcon_flds = fs}) = getFV fs- getFV (ArithSeq _ _ e) = getFV e-#if __GLASGOW_HASKELL__ >= 906- getFV HsTypedSplice{} = Set.empty- getFV HsUntypedSplice{} = Set.empty-#else- getFV HsSpliceE{} = Set.empty-#endif- getFV (HsProc _ _ e) = getFV e- getFV (HsStatic _ e) = getFV e- getFV (XExpr e) = getFV e-#if __GLASGOW_HASKELL__ >= 904- getFV (HsPar _ _ e _) = getFV e - getFV (HsLamCase _ _ mg) = getFV mg- getFV (HsLet _ _ bs _ e) = getFV bs `Set.union` getFV e- getFV HsRecSel {} = Set.empty- getFV (HsTypedBracket _ e) = getFV e- getFV (HsUntypedBracket _ e) = getFV e-#else - getFV (HsBinTick _ _ _ e) = getFV e- getFV (HsTick _ _ e) = getFV e- getFV (HsLet _ bs e) = getFV bs `Set.union` getFV e- getFV (HsPar _ e) = getFV e- getFV (HsLamCase _ mg) = getFV mg- getFV HsConLikeOut {} = Set.empty- getFV HsRecFld {} = Set.empty- getFV (HsBracket _ e) = getFV e- getFV HsRnBracketOut {} = Set.empty- getFV HsTcBracketOut {} = Set.empty-#endif--#if __GLASGOW_HASKELL__ >= 906- getFV (HsAppType _ e _ _) = getFV e- getFV (ExprWithTySig _ e _) = getFV e -#else- getFV (HsAppType _ e _) = getFV e- getFV (ExprWithTySig _ e _) = getFV e -#endif- getFV (HsIf _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3- getFV (HsPragE _ _ e) = getFV e---instance HasFV XXExprGhcTc where- getFV (WrapExpr e) = getFV e- getFV (ExpansionExpr (HsExpanded _e1 e2)) = getFV e2-#if __GLASGOW_HASKELL__ >= 904 - getFV (HsTick _ e) = getFV e- getFV (HsBinTick _ _ e) = getFV e- getFV ConLikeTc{} = Set.empty-#endif---instance HasFV (e GhcTc) => HasFV (HsWrap e) where- getFV (HsWrap _ e) = getFV e
− src/AsyncRattus/Plugin/PrimExpr.hs
@@ -1,109 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module AsyncRattus.Plugin.PrimExpr (- Prim (..),- PrimInfo (..),- function,- prim,- isPrimExpr-) where--import Data.Map (Map)-import qualified Data.Map as Map-import GHC.Plugins-import AsyncRattus.Plugin.Utils-import Prelude hiding ((<>))--data Prim = Delay | Adv | Box | Select---- DelayApp has the following fields: Var = delay f, T1 = value type, T2 = later type (O v a)--- AdvApp has the following fields: Var = adv f, TypedArg = var and type for arg-data PrimInfo = DelayApp Var Type | AdvApp Var TypedArg | BoxApp Var | SelectApp Var TypedArg TypedArg--type TypedArg = (Var, Type)--data PartialPrimInfo = PartialPrimInfo {- primPart :: Prim,- functionPart :: Var,- args :: [Var],- typeArgs :: [Type]-}--instance Outputable PartialPrimInfo where- ppr (PartialPrimInfo Delay f _ typeArgs) = text "PartialPrimInfo { prim = Delay, function = " <> ppr f <> text "args = (not printing since it should be undefined) , typeArgs = " <> ppr typeArgs - ppr (PartialPrimInfo prim f args typeArgs) = text "PartialPrimInfo { prim = " <> ppr prim <> text ", function = " <> ppr f <> text ", args = " <> ppr args <> text ", typeArgs = " <> ppr typeArgs--instance Outputable Prim where- ppr Delay = "delay"- ppr Adv = "adv"- ppr Select = "select"- ppr Box = "box"--instance Outputable PrimInfo where- ppr (DelayApp f _) = text "DelayApp - function " <> ppr f - ppr (BoxApp f) = text "BoxApp - function " <> ppr f- ppr (AdvApp f arg) = text "AdvApp - function " <> ppr f <> text " | arg " <> ppr arg- ppr (SelectApp f arg arg2) = text "SelectApp - function " <> ppr f <> text " | arg " <> ppr arg <> text " | arg2 " <> ppr arg2- -primMap :: Map FastString Prim-primMap = Map.fromList- [("delay", Delay),- ("adv", Adv),- ("select", Select),- ("box", Box)- ]---isPrim :: Var -> Maybe Prim-isPrim v = case getNameModule v of- Just (name, mod) | isRattModule mod -> Map.lookup name primMap- _ -> Nothing--createPartialPrimInfo :: Prim -> Var -> PartialPrimInfo-createPartialPrimInfo prim function =- PartialPrimInfo {- primPart = prim,- functionPart = function,- args = [],- typeArgs = []- }--function :: PrimInfo -> Var-function (DelayApp f _) = f-function (BoxApp f) = f-function (AdvApp f _) = f-function (SelectApp f _ _) = f--prim :: PrimInfo -> Prim-prim (DelayApp {}) = Delay-prim (BoxApp _) = Box-prim (AdvApp {}) = Adv-prim (SelectApp {}) = Select--validatePartialPrimInfo :: PartialPrimInfo -> Maybe PrimInfo-validatePartialPrimInfo (PartialPrimInfo Select f [arg2V, argV] [arg2T, argT]) = Just $ SelectApp f (argV, argT) (arg2V, arg2T)-validatePartialPrimInfo (PartialPrimInfo Delay f [_] [argT]) = Just $ DelayApp f argT-validatePartialPrimInfo (PartialPrimInfo {primPart = Box, functionPart = f}) = Just $ BoxApp f-validatePartialPrimInfo (PartialPrimInfo Adv f [argV] [argT]) = Just $ AdvApp f (argV, argT)-validatePartialPrimInfo _ = Nothing--isPrimExpr :: Expr Var -> Maybe PrimInfo-isPrimExpr expr = isPrimExpr' expr >>= validatePartialPrimInfo--isPrimExpr' :: Expr Var -> Maybe PartialPrimInfo-isPrimExpr' (App e (Type t)) = case mPPI of- Just pPI@(PartialPrimInfo {typeArgs = tArgs}) -> Just pPI {typeArgs = t : tArgs}- Nothing -> Nothing- where mPPI = isPrimExpr' e-isPrimExpr' (App e e') =- case isPrimExpr' e of- Just partPrimInfo@(PartialPrimInfo { primPart = Delay, args = args}) -> Just partPrimInfo {args = undefined : args}- Just partPrimInfo@(PartialPrimInfo { args = args}) -> Just partPrimInfo {args = maybe args (:args) (getMaybeVar e')}- _ -> Nothing-isPrimExpr' (Var v) = case isPrim v of- Just p -> Just $ createPartialPrimInfo p v- Nothing -> Nothing-isPrimExpr' (Tick _ e) = isPrimExpr' e-isPrimExpr' (Lam v e)- | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = isPrimExpr' e-isPrimExpr' _ = Nothing
− src/AsyncRattus/Plugin/ScopeCheck.hs
@@ -1,812 +0,0 @@-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ImplicitParams #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE CPP #-}------ | This module implements the source plugin that checks the variable--- scope of of Async Rattus programs.--module AsyncRattus.Plugin.ScopeCheck (checkAll) where--import AsyncRattus.Plugin.Utils-import AsyncRattus.Plugin.Dependency-import AsyncRattus.Plugin.Annotation--import Control.Monad.Trans.State.Strict-import Data.IORef--import Prelude hiding ((<>))--import GHC.Parser.Annotation-import GHC.Plugins-import GHC.Tc.Types-import GHC.Data.Bag-import GHC.Tc.Types.Evidence-import GHC.Hs.Extension-import GHC.Hs.Expr-import GHC.Hs.Pat-import GHC.Hs.Binds--import Data.Graph-import qualified Data.Set as Set-import qualified Data.Map as Map-import Data.Set (Set)-import Data.Map (Map)-import Data.List-import Data.List.NonEmpty (NonEmpty(..),(<|),nonEmpty)-import System.Exit-import Data.Either-import Data.Maybe--import Data.Data hiding (tyConName)--import Control.Monad--type ErrorMsg = (Severity,SrcSpan,SDoc)-type ErrorMsgsRef = IORef [ErrorMsg]---- | The current context for scope checking-data Ctxt = Ctxt- {- errorMsgs :: ErrorMsgsRef,- -- | Variables that are in scope now (i.e. occurring in the typing- -- context but not to the left of a tick)- current :: LCtxt,- -- | Variables that are in the typing context, but to the left of a- -- tick- earlier :: Either NoTickReason (NonEmpty LCtxt),- -- | Variables that have fallen out of scope. The map contains the- -- reason why they have fallen out of scope.- hidden :: Hidden,- -- -- | Same as 'hidden' but for recursive variables.- -- hiddenRec :: Hidden,- -- | The current location information.- srcLoc :: SrcSpan,- -- | If we are in the body of a recursively defined function, this- -- field contains the variables that are defined recursively- -- (could be more than one due to mutual recursion or because of a- -- recursive pattern definition) and the location of the recursive- -- definition.- recDef :: Maybe RecDef,- -- | Type variables with a 'Stable' constraint attached to them.- stableTypes :: Set Var,- -- | A mapping from variables to the primitives that they are- -- defined equal to. For example, a program could contain @let- -- mydel = delay in mydel 1@, in which case @mydel@ is mapped to- -- 'Delay'.- primAlias :: Map Var Prim,- -- | Allow general recursion.- allowRecursion :: Bool}------ | The starting context for checking a top-level definition. For--- non-recursive definitions, the argument is @Nothing@. Otherwise, it--- contains the recursively defined variables along with the location--- of the recursive definition.-emptyCtxt :: ErrorMsgsRef -> Maybe (Set Var,SrcSpan) -> Bool -> Ctxt-emptyCtxt em mvar allowRec =- Ctxt { errorMsgs = em,- current = Set.empty,- earlier = Left NoDelay,- hidden = Map.empty,- srcLoc = noLocationInfo,- recDef = mvar,- primAlias = Map.empty,- stableTypes = Set.empty,- allowRecursion = allowRec}---- | A local context, consisting of a set of variables.-type LCtxt = Set Var---- | The recursively defined variables + the position where the--- recursive definition starts-type RecDef = (Set Var, SrcSpan)-----data StableReason = StableRec SrcSpan | StableBox deriving Show---- | Indicates, why a variable has fallen out of scope.-data HiddenReason = Stabilize StableReason | FunDef | DelayApp | AdvApp | SelectApp deriving Show---- | Indicates, why there is no tick-data NoTickReason = NoDelay | TickHidden HiddenReason deriving Show---- | Hidden context, containing variables that have fallen out of--- context along with the reason why they have.-type Hidden = Map Var HiddenReason---- | The 5 primitive Asynchronous Rattus operations.-data Prim = Delay | Adv | Select | Box | Unbox deriving Show---- | This constraint is used to pass along the context implicitly via--- an implicit parameter.-type GetCtxt = ?ctxt :: Ctxt---type CheckM = StateT ([Maybe (Prim, SrcSpan)]) TcM---- | This type class is implemented for each AST type @a@ for which we--- can check whether it adheres to the scoping rules of Asynchronous Rattus.-class Scope a where- -- | Check whether the argument is a scope correct piece of syntax- -- in the given context.- check :: GetCtxt => a -> CheckM Bool---- | This is a variant of 'Scope' for syntax that can also bind--- variables.-class ScopeBind a where- -- | 'checkBind' checks whether its argument is scope-correct and in- -- addition returns the the set of variables bound by it.- checkBind :: GetCtxt => a -> CheckM (Bool,Set Var)----- | set the current context.-setCtxt :: Ctxt -> (GetCtxt => a) -> a -setCtxt c a = let ?ctxt = c in a----- | modify the current context.-modifyCtxt :: (Ctxt -> Ctxt) -> (GetCtxt => a) -> (GetCtxt => a)-modifyCtxt f a =- let newc = f ?ctxt in- let ?ctxt = newc in a-----getLocAnn' :: SrcSpanAnn' b -> SrcSpan-getLocAnn' = locA---updateLoc :: SrcSpanAnn' b -> (GetCtxt => a) -> (GetCtxt => a)-updateLoc src = modifyCtxt (\c -> c {srcLoc = getLocAnn' src})----- | Check all definitions in the given module. If Scope errors are--- found, the current execution is halted with 'exitFailure'.-checkAll :: TcGblEnv -> TcM ()-checkAll env = do- let bindDep = dependency (tcg_binds env)- result <- mapM (checkSCC' (tcg_mod env) (tcg_ann_env env)) bindDep- let (res,msgs) = foldl' (\(b,l) (b',l') -> (b && b', l ++ l')) (True,[]) result- printAccErrMsgs msgs- if res then return () else liftIO exitFailure---printAccErrMsgs :: [ErrorMsg] -> TcM ()-printAccErrMsgs msgs = mapM_ printMsg (sortOn (\(_,l,_)->l) msgs)- where printMsg (sev,loc,doc) = printMessage sev loc doc-----instance Scope a => Scope (GenLocated SrcSpan a) where- check (L l x) = (\c -> c {srcLoc = l}) `modifyCtxt` check x--instance Scope a => Scope (GenLocated (SrcSpanAnn' b) a) where- check (L l x) = updateLoc l $ check x- -instance Scope a => Scope (Bag a) where- check bs = fmap and (mapM check (bagToList bs))--instance Scope a => Scope [a] where- check ls = fmap and (mapM check ls)---instance Scope (Match GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where- check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs--instance Scope (Match GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where- check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs---instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where- check MG {mg_alts = alts} = check alts---instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where- check MG {mg_alts = alts} = check alts---instance Scope a => ScopeBind (StmtLR GhcTc GhcTc a) where- checkBind (LastStmt _ b _ _) = ( , Set.empty) <$> check b- checkBind (BindStmt _ p b) = do- let vs = getBV p- let c' = addVars vs ?ctxt- r <- setCtxt c' (check b)- return (r,vs)- checkBind (BodyStmt _ b _ _) = ( , Set.empty) <$> check b- checkBind (LetStmt _ bs) = checkBind bs- checkBind ParStmt{} = notSupported "monad comprehensions"- checkBind TransStmt{} = notSupported "monad comprehensions"- checkBind ApplicativeStmt{} = notSupported "applicative do notation"- checkBind RecStmt{} = notSupported "recursive do notation"--instance ScopeBind a => ScopeBind [a] where- checkBind [] = return (True,Set.empty)- checkBind (x:xs) = do- (r,vs) <- checkBind x- (r',vs') <- addVars vs `modifyCtxt` (checkBind xs)- return (r && r',vs `Set.union` vs')--instance ScopeBind a => ScopeBind (GenLocated SrcSpan a) where- checkBind (L l x) = (\c -> c {srcLoc = l}) `modifyCtxt` checkBind x--instance ScopeBind a => ScopeBind (GenLocated (SrcSpanAnn' b) a) where- checkBind (L l x) = updateLoc l $ checkBind x--instance Scope a => Scope (GRHS GhcTc a) where- check (GRHS _ gs b) = do- (r, vs) <- checkBind gs- r' <- addVars vs `modifyCtxt` (check b)- return (r && r')--checkRec :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool-checkRec b = liftM2 (&&) (checkPatBind b) (check b)--checkPatBind :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool-checkPatBind (L l b) = updateLoc l $ checkPatBind' b--checkPatBind' :: GetCtxt => HsBindLR GhcTc GhcTc -> CheckM Bool-checkPatBind' PatBind{} = do- printMessage' SevError ("(Mutual) recursive pattern binding definitions are not supported in Asynchronous Rattus")- return False-#if __GLASGOW_HASKELL__ < 904-checkPatBind' AbsBinds {abs_binds = binds} = -#else-checkPatBind' (XHsBindsLR AbsBinds {abs_binds = binds}) = -#endif- liftM and (mapM checkPatBind (bagToList binds))--checkPatBind' _ = return True----- | Check the scope of a list of (mutual) recursive bindings. The--- second argument is the set of variables defined by the (mutual)--- recursive bindings-checkRecursiveBinds :: GetCtxt => [LHsBindLR GhcTc GhcTc] -> Set Var -> CheckM (Bool, Set Var)-checkRecursiveBinds bs vs = do- res <- fmap and (mapM check' bs)- return (res, vs)- where check' b@(L l _) = fc (getLocAnn' l) `modifyCtxt` checkRec b- fc l c = let- ctxHid = either (const $ current c) (Set.union (current c) . Set.unions) (earlier c)- in c {current = Set.empty,- earlier = Left (TickHidden $ Stabilize $ StableRec l),- hidden = hidden c `Map.union`- (Map.fromSet (const (Stabilize (StableRec l))) ctxHid),- recDef = maybe (Just (vs,l)) (\(vs',_) -> Just (Set.union vs' vs,l)) (recDef c)- -- TODO fix location info of recDef (needs one location for each var)- } ---instance ScopeBind (SCC (GenLocated SrcSpanAnnA (HsBindLR GhcTc GhcTc), Set Var)) where- checkBind (AcyclicSCC (b,vs)) = (, vs) <$> check b- checkBind (CyclicSCC bs) = checkRecursiveBinds (map fst bs) (foldMap snd bs)- -instance ScopeBind (HsValBindsLR GhcTc GhcTc) where- checkBind (ValBinds _ bs _) = checkBind (dependency bs)- - checkBind (XValBindsLR (NValBinds binds _)) = checkBind binds---instance ScopeBind (HsBindLR GhcTc GhcTc) where- checkBind b = (, getBV b) <$> check b----- | Compute the set of variables defined by the given Haskell binder.-getAllBV :: GenLocated l (HsBindLR GhcTc GhcTc) -> Set Var-getAllBV (L _ b) = getAllBV' b where- getAllBV' (FunBind{fun_id = L _ v}) = Set.singleton v-#if __GLASGOW_HASKELL__ < 904- getAllBV' (AbsBinds {abs_exports = es, abs_binds = bs}) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs- getAllBV' XHsBindsLR{} = Set.empty-#else- getAllBV' (XHsBindsLR (AbsBinds {abs_exports = es, abs_binds = bs})) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs-#endif- getAllBV' (PatBind {pat_lhs = pat}) = getBV pat- getAllBV' (VarBind {var_id = v}) = Set.singleton v- getAllBV' PatSynBind{} = Set.empty----- Check nested bindings-instance ScopeBind (RecFlag, Bag (GenLocated SrcSpanAnnA (HsBindLR GhcTc GhcTc))) where- checkBind (NonRecursive, bs) = checkBind $ bagToList bs- checkBind (Recursive, bs) = checkRecursiveBinds bs' (foldMap getAllBV bs')- where bs' = bagToList bs---instance ScopeBind (HsLocalBindsLR GhcTc GhcTc) where- checkBind (HsValBinds _ bs) = checkBind bs- checkBind HsIPBinds {} = notSupported "implicit parameters"- checkBind EmptyLocalBinds{} = return (True,Set.empty)--type SrcAnno = SrcSpanAnnA- -instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where- check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do- (l,vs) <- checkBind lbinds- r <- addVars vs `modifyCtxt` (check rhs)- return (r && l)--instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where- check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do- (l,vs) <- checkBind lbinds- r <- addVars vs `modifyCtxt` (check rhs)- return (r && l)--instance Show Var where- show v = getOccString v---tickHidden :: HiddenReason -> SDoc-tickHidden FunDef = "a function definition"-tickHidden DelayApp = "a nested application of delay"-tickHidden AdvApp = "an application of adv"-tickHidden SelectApp = "an application of select"-tickHidden (Stabilize StableBox) = "an application of box"-tickHidden (Stabilize (StableRec src)) = "a nested recursive definition (at " <> ppr src <> ")"--isSelect :: GetCtxt => LHsExpr GhcTc -> Bool-isSelect e =- case isPrimExpr e of- Just (Select, _) -> True- _ -> False--instance Scope (HsExpr GhcTc) where- check (HsVar _ (L _ v))- | Just p <- isPrim v =- case p of- Unbox -> return True- _ -> printMessageCheck SevError ("Defining an alias for " <> ppr v <> " is not allowed")- | otherwise = case getScope v of- Hidden reason -> printMessageCheck SevError reason- Visible -> return True- ImplUnboxed -> return True- -- printMessageCheck SevWarning- -- (ppr v <> text " is an external temporal function used under delay, which may cause time leaks.")- check (HsApp _ (L _ (HsApp _ f arg)) arg2) | isSelect f =- case earlier ?ctxt of- Right (er :| ers) -> do- res <- get- case res of- Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")- Nothing : pre -> do put pre- b1 <- mod `modifyCtxt` check arg- b2 <- mod `modifyCtxt` check arg2- modify (Just (Select, srcLoc ?ctxt) :)- return $ b1 && b2- _ -> error "Asynchronous Rattus: internal error"- where mod c = c{earlier = case nonEmpty ers of- Nothing -> Left $ TickHidden SelectApp- Just ers' -> Right ers',- current = er,- hidden = hidden ?ctxt `Map.union`- Map.fromSet (const SelectApp) (current ?ctxt)}- Left NoDelay -> printMessageCheck SevError "select may only be used in the scope of a delay."- Left (TickHidden hr) -> printMessageCheck SevError ("select may only be used in the scope of a delay. "- <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")- check (HsApp _ e1 e2) =- case isPrimExpr e1 of- Just (p,_) -> case p of- Box -> do- ch <- stabilize StableBox `modifyCtxt` check e2- return ch- Unbox -> check e2- Delay -> do modify (Nothing :)- b <- (\c -> c{current = Set.empty,- earlier = case earlier c of- Left _ -> Right (current c :| [])- Right cs -> Right (current c <| cs)})- `modifyCtxt` check e2- res <- get- case res of- Nothing : _ -> printMessageCheck SevError "No adv or select found in the scope of this occurrence of delay"- _ : pre -> put pre >> return b- _ -> error "Asynchronous Rattus: internal error"- Adv -> case earlier ?ctxt of- Right (er :| ers) -> do- res <- get- case res of- Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")- Nothing : pre -> do put pre- b <- mod `modifyCtxt` check e2- modify (Just (Adv,srcLoc ?ctxt) :)- return b- _ -> error "Asynchronous Rattus: internal error"- where mod c = c{earlier = case nonEmpty ers of- Nothing -> Left $ TickHidden AdvApp- Just ers' -> Right ers',- current = er,- hidden = hidden ?ctxt `Map.union`- Map.fromSet (const AdvApp) (current ?ctxt)}- Left NoDelay -> printMessageCheck SevError ("adv may only be used in the scope of a delay.")- Left (TickHidden hr) -> printMessageCheck SevError ("adv may only be used in the scope of a delay. "- <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")- Select -> printMessageCheck SevError ("select must be fully applied")- _ -> liftM2 (&&) (check e1) (check e2)- check HsUnboundVar{} = return True-#if __GLASGOW_HASKELL__ >= 904- check (HsPar _ _ e _) = check e- check (HsLamCase _ _ mg) = check mg- check HsRecSel{} = return True- check HsTypedBracket{} = notSupported "MetaHaskell"- check HsUntypedBracket{} = notSupported "MetaHaskell"-#else- check HsConLikeOut{} = return True- check HsRecFld{} = return True- check (HsPar _ e) = check e- check (HsLamCase _ mg) = check mg- check HsBracket{} = notSupported "MetaHaskell"- check (HsTick _ _ e) = check e- check (HsBinTick _ _ _ e) = check e- check HsRnBracketOut{} = notSupported "MetaHaskell"- check HsTcBracketOut{} = notSupported "MetaHaskell"-#endif-#if __GLASGOW_HASKELL__ >= 904- check (HsLet _ _ bs _ e) = do-#else- check (HsLet _ bs e) = do-#endif- (l,vs) <- checkBind bs- r <- addVars vs `modifyCtxt` (check e)- return (r && l)- - check HsOverLabel{} = return True- check HsIPVar{} = notSupported "implicit parameters"- check HsOverLit{} = return True - check HsLit{} = return True- check (OpApp _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]- check (HsLam _ mg) = check mg- check (HsCase _ e1 e2) = (&&) <$> check e1 <*> check e2- check (SectionL _ e1 e2) = (&&) <$> check e1 <*> check e2- check (SectionR _ e1 e2) = (&&) <$> check e1 <*> check e2- check (ExplicitTuple _ e _) = check e- check (NegApp _ e _) = check e- check (ExplicitSum _ _ _ e) = check e- check (HsMultiIf _ e) = check e- check (ExplicitList _ e) = check e- check HsProjection {} = return True- check HsGetField {gf_expr = e} = check e- check RecordUpd { rupd_expr = e, rupd_flds = fs} = (&&) <$> check e <*> check fs- check RecordCon { rcon_flds = f} = check f- check (ArithSeq _ _ e) = check e-#if __GLASGOW_HASKELL__ >= 906- check HsTypedSplice{} = notSupported "Template Haskell"- check HsUntypedSplice{} = notSupported "Template Haskell"-#else- check HsSpliceE{} = notSupported "Template Haskell"-#endif- check (HsProc _ _ e) = check e- check (HsStatic _ e) = check e- check (HsDo _ _ e) = fst <$> checkBind e- check (XExpr e) = check e-#if __GLASGOW_HASKELL__ >= 906- check (HsAppType _ e _ _) = check e- check (ExprWithTySig _ e _) = check e-#else- check (HsAppType _ e _) = check e- check (ExprWithTySig _ e _) = check e-#endif- check (HsPragE _ _ e) = check e- check (HsIf _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]---instance (Scope a, Scope b) => Scope (Either a b) where- check (Left x) = check x- check (Right x) = check x---#if __GLASGOW_HASKELL__ >= 908-instance Scope (LHsRecUpdFields GhcTc) where- check RegularRecUpdFields {recUpdFields = x} = check x- check OverloadedRecUpdFields {olRecUpdFields = x} = check x-#endif---instance Scope XXExprGhcTc where- check (WrapExpr (HsWrap _ e)) = check e- check (ExpansionExpr (HsExpanded _ e)) = check e-#if __GLASGOW_HASKELL__ >= 904- check ConLikeTc{} = return True- check (HsTick _ e) = check e- check (HsBinTick _ _ e) = check e-#endif--instance Scope (HsCmdTop GhcTc) where- check (HsCmdTop _ e) = check e- -instance Scope (HsCmd GhcTc) where- check (HsCmdArrApp _ e1 e2 _ _) = (&&) <$> check e1 <*> check e2- check (HsCmdDo _ e) = fst <$> checkBind e- check (HsCmdArrForm _ e1 _ _ e2) = (&&) <$> check e1 <*> check e2- check (HsCmdApp _ e1 e2) = (&&) <$> check e1 <*> check e2- check (HsCmdLam _ e) = check e-#if __GLASGOW_HASKELL__ >= 904- check (HsCmdPar _ _ e _) = check e- check (HsCmdLamCase _ _ e) = check e - check (HsCmdLet _ _ bs _ e) = do-#else- check (HsCmdPar _ e) = check e- check (HsCmdLamCase _ e) = check e- check (HsCmdLet _ bs e) = do-#endif- (l,vs) <- checkBind bs- r <- addVars vs `modifyCtxt` (check e)- return (r && l)-- check (HsCmdCase _ e1 e2) = (&&) <$> check e1 <*> check e2- check (HsCmdIf _ _ e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3- check (XCmd (HsWrap _ e)) = check e---instance Scope (ArithSeqInfo GhcTc) where- check (From e) = check e- check (FromThen e1 e2) = (&&) <$> check e1 <*> check e2- check (FromTo e1 e2) = (&&) <$> check e1 <*> check e2- check (FromThenTo e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3--instance Scope a => Scope (HsRecFields GhcTc a) where- check HsRecFields {rec_flds = fs} = check fs----#if __GLASGOW_HASKELL__ >= 904-instance Scope b => Scope (HsFieldBind a b) where- check HsFieldBind{hfbRHS = a} = check a-#else-instance Scope b => Scope (HsRecField' a b) where- check HsRecField{hsRecFieldArg = a} = check a-#endif--instance Scope (HsTupArg GhcTc) where- check (Present _ e) = check e- check Missing{} = return True--instance Scope (HsBindLR GhcTc GhcTc) where-#if __GLASGOW_HASKELL__ >= 904- check (XHsBindsLR AbsBinds {abs_binds = binds, abs_ev_vars = ev})-#else- check AbsBinds {abs_binds = binds, abs_ev_vars = ev}-#endif- = mod `modifyCtxt` check binds- where mod c = c { stableTypes= stableTypes c `Set.union`- Set.fromList (mapMaybe (isStableConstr . varType) ev)}- check FunBind{fun_matches= matches, fun_id = L _ v,- fun_ext = wrapper} =- mod `modifyCtxt` check matches- where mod c = c { stableTypes= stableTypes c `Set.union`- Set.fromList (stableConstrFromWrapper' wrapper) `Set.union`- Set.fromList (extractStableConstr (varType v))}- check PatBind{pat_lhs = lhs, pat_rhs=rhs} = addVars (getBV lhs) `modifyCtxt` check rhs- check VarBind{var_rhs = rhs} = check rhs- check PatSynBind {} = return True -- pattern synonyms are not supported----- | Checks whether the given type is a type constraint of the form--- @Stable a@ for some type variable @a@. In that case it returns the--- type variable @a@.-isStableConstr :: Type -> Maybe TyVar-isStableConstr t = - case splitTyConApp_maybe t of- Just (con,[args]) ->- case getNameModule con of- Just (name, mod) ->- if isRattModule mod && name == "Stable"- then (getTyVar_maybe args)- else Nothing- _ -> Nothing - _ -> Nothing----#if __GLASGOW_HASKELL__ >= 906-stableConstrFromWrapper' :: (HsWrapper , a) -> [TyVar]-stableConstrFromWrapper' (x , _) = stableConstrFromWrapper x-#else-stableConstrFromWrapper' :: HsWrapper -> [TyVar]-stableConstrFromWrapper' = stableConstrFromWrapper-#endif--stableConstrFromWrapper :: HsWrapper -> [TyVar]-stableConstrFromWrapper (WpCompose v w) = stableConstrFromWrapper v ++ stableConstrFromWrapper w-stableConstrFromWrapper (WpEvLam v) = maybeToList $ isStableConstr (varType v)-stableConstrFromWrapper _ = []----- | Given a type @(C1, ... Cn) => t@, this function returns the list--- of type variables @[a1,...,am]@ for which there is a constraint--- @Stable ai@ among @C1, ... Cn@.-extractStableConstr :: Type -> [TyVar]-extractStableConstr = mapMaybe isStableConstr . map irrelevantMult . fst . splitFunTys . snd . splitForAllTys'---getSCCLoc :: SCC (LHsBindLR GhcTc GhcTc, Set Var) -> SrcSpan-getSCCLoc (AcyclicSCC (L l _ ,_)) = getLocAnn' l-getSCCLoc (CyclicSCC ((L l _,_ ) : _)) = getLocAnn' l-getSCCLoc _ = noLocationInfo--checkSCC' :: Module -> AnnEnv -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> TcM (Bool, [ErrorMsg])-checkSCC' mod anEnv scc = do- err <- liftIO (newIORef [])- let allowRec = AllowRecursion `Set.member` getAnn mod anEnv scc- res <- checkSCC allowRec err scc- msgs <- liftIO (readIORef err)- let anns = getAnn mod anEnv scc- if ExpectWarning `Set.member` anns - then if ExpectError `Set.member` anns- then return (False,[(SevError, getSCCLoc scc, "Annotation to expect both warning and error is not allowed.")])- else if any (\(s,_,_) -> case s of SevWarning -> True; _ -> False) msgs- then return (res, filter (\(s,_,_) -> case s of SevWarning -> False; _ -> True) msgs)- else return (False,[(SevError, getSCCLoc scc, "Warning was expected, but typechecking produced no warning.")])- else if ExpectError `Set.member` anns- then if res- then return (False,[(SevError, getSCCLoc scc, "Error was expected, but typechecking produced no error.")])- else return (True,[])- else return (res, msgs)-getAnn :: forall a . (Data a, Ord a) => Module -> AnnEnv -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> Set a-getAnn mod anEnv scc =- case scc of- (AcyclicSCC (_,vs)) -> Set.unions $ Set.map checkVar vs- (CyclicSCC bs) -> Set.unions $ map (Set.unions . Set.map checkVar . snd) bs- where checkVar :: Var -> Set a- checkVar v =- let anns = findAnns deserializeWithData anEnv (NamedTarget name) :: [a]- annsMod = findAnns deserializeWithData anEnv (ModuleTarget mod) :: [a]- name :: Name- name = varName v- in Set.fromList anns `Set.union` Set.fromList annsMod------ | Checks a top-level definition group, which is either a single--- non-recursive definition or a group of (mutual) recursive--- definitions.--checkSCC :: Bool -> ErrorMsgsRef -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> TcM Bool-checkSCC allowRec errm (AcyclicSCC (b,_)) = setCtxt (emptyCtxt errm Nothing allowRec) (evalStateT (check b) [])--checkSCC allowRec errm (CyclicSCC bs) = (fmap and (mapM check' bs'))- where bs' = map fst bs- vs = foldMap snd bs- check' b@(L l _) = setCtxt (emptyCtxt errm (Just (vs,getLocAnn' l)) allowRec) (evalStateT (checkRec b) [])---- | Stabilizes the given context, i.e. remove all non-stable types--- and any tick. This is performed on checking 'box', and--- guarded recursive definitions. To provide better error messages a--- reason has to be given as well.-stabilize :: StableReason -> Ctxt -> Ctxt-stabilize sr c = c- {current = Set.empty,- earlier = Left $ TickHidden hr,- hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid}- where ctxHid = either (const $ current c) (foldl' Set.union (current c)) (earlier c)- hr = Stabilize sr--data VarScope = Hidden SDoc | Visible | ImplUnboxed----- | This function checks whether the given variable is in scope.-getScope :: GetCtxt => Var -> VarScope-getScope v =- case ?ctxt of- Ctxt{recDef = Just (vs,_), earlier = e, allowRecursion = allowRec} | v `Set.member` vs ->- if allowRec then Visible else- case e of- Right _ -> Visible- Left NoDelay -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay")- Left (TickHidden hr) -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay. "- <> "There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")- _ -> case Map.lookup v (hidden ?ctxt) of- Just (Stabilize (StableRec rv)) ->- if (isStable (stableTypes ?ctxt) (varType v)) || allowRecursion ?ctxt then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$- "It appears in a local recursive definition (at " <> ppr rv <> ")"- $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- Just (Stabilize StableBox) ->- if (isStable (stableTypes ?ctxt) (varType v)) then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$- "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- Just AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")- Just SelectApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under select.")- Just DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope due to repeated application of delay")- Just FunDef -> if (isStable (stableTypes ?ctxt) (varType v)) then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")- Nothing- | either (const False) (any (Set.member v)) (earlier ?ctxt) ->- if isStable (stableTypes ?ctxt) (varType v) then Visible- else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$- "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")- | Set.member v (current ?ctxt) -> Visible- | isTemporal (varType v) && isRight (earlier ?ctxt) && userFunction v- -> ImplUnboxed- | otherwise -> Visible---- | A map from the syntax of a primitive of Asynchronous Rattus to 'Prim'.-primMap :: Map FastString Prim-primMap = Map.fromList- [("Delay", Delay),- ("delay", Delay),- ("adv", Adv),- ("select", Select),- ("box", Box),- ("unbox", Unbox)]----- | Checks whether a given variable is in fact an Asynchronous Rattus primitive.-isPrim :: GetCtxt => Var -> Maybe Prim-isPrim v- | Just p <- Map.lookup v (primAlias ?ctxt) = Just p- | otherwise = do- (name,mod) <- getNameModule v- if isRattModule mod then Map.lookup name primMap- else Nothing----- | Checks whether a given expression is in fact a Asynchronous Rattus primitive.-isPrimExpr :: GetCtxt => LHsExpr GhcTc -> Maybe (Prim,Var)-isPrimExpr (L _ e) = isPrimExpr' e where- isPrimExpr' :: GetCtxt => HsExpr GhcTc -> Maybe (Prim,Var)- isPrimExpr' (HsVar _ (L _ v)) = fmap (,v) (isPrim v)-#if __GLASGOW_HASKELL__ >= 906- isPrimExpr' (HsAppType _ e _ _) = isPrimExpr e-#else- isPrimExpr' (HsAppType _ e _) = isPrimExpr e-#endif-- isPrimExpr' (XExpr (WrapExpr (HsWrap _ e))) = isPrimExpr' e- isPrimExpr' (XExpr (ExpansionExpr (HsExpanded _ e))) = isPrimExpr' e- isPrimExpr' (HsPragE _ _ e) = isPrimExpr e-#if __GLASGOW_HASKELL__ < 904- isPrimExpr' (HsTick _ _ e) = isPrimExpr e- isPrimExpr' (HsBinTick _ _ _ e) = isPrimExpr e- isPrimExpr' (HsPar _ e) = isPrimExpr e-#else- isPrimExpr' (XExpr (HsTick _ e)) = isPrimExpr e- isPrimExpr' (XExpr (HsBinTick _ _ e)) = isPrimExpr e- isPrimExpr' (HsPar _ _ e _) = isPrimExpr e-#endif-- isPrimExpr' _ = Nothing----- | This type class provides default implementations for 'check' and--- 'checkBind' for Haskell syntax that is not supported. These default--- implementations simply print an error message.-class NotSupported a where- notSupported :: GetCtxt => SDoc -> CheckM a--instance NotSupported Bool where- notSupported doc = printMessageCheck SevError ("Asynchronous Rattus does not support " <> doc)--instance NotSupported (Bool,Set Var) where- notSupported doc = (,Set.empty) <$> notSupported doc----- | Add variables to the current context.-addVars :: Set Var -> Ctxt -> Ctxt-addVars vs c = c{current = vs `Set.union` current c }---- | Print a message with the current location.-printMessage' :: GetCtxt => Severity -> SDoc -> CheckM ()-printMessage' sev doc =- liftIO (modifyIORef (errorMsgs ?ctxt) ((sev ,srcLoc ?ctxt, doc) :))---- | Print a message with the current location. Returns 'False', if--- the severity is 'SevError' and otherwise 'True.-printMessageCheck :: GetCtxt => Severity -> SDoc -> CheckM Bool-printMessageCheck sev doc = printMessage' sev doc >>- case sev of- SevError -> return False- _ -> return True
− src/AsyncRattus/Plugin/SingleTick.hs
@@ -1,226 +0,0 @@--- | This module implements the translation from the multi-tick--- calculus to the single tick calculus.--{-# LANGUAGE CPP #-}--module AsyncRattus.Plugin.SingleTick- (toSingleTick) where--#if __GLASGOW_HASKELL__ >= 900-import GHC.Plugins-#else-import GhcPlugins-#endif-- -import AsyncRattus.Plugin.Utils-import Prelude hiding ((<>))-import Control.Monad.Trans.Writer.Strict-import Control.Monad.Trans.Class-import Data.List---- | Transform the given expression from the multi-tick calculus into--- the single tick calculus form.-toSingleTick :: CoreExpr -> CoreM CoreExpr-toSingleTick (Let (Rec bs) e) = do- e' <- toSingleTick e- bs' <- mapM (mapM toSingleTick) bs- return (Let (Rec bs') e')-toSingleTick (Let (NonRec b e1) e2) = do- e1' <- toSingleTick e1- e2' <- toSingleTick e2- return (Let (NonRec b e1') e2')-toSingleTick (Case e b ty alts) = do- e' <- toSingleTick e- alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (toSingleTick f)) . getAlt) alts- return (Case e' b ty alts')-toSingleTick (Cast e c) = do- e' <- toSingleTick e- return (Cast e' c)-toSingleTick (Tick t e) = do- e' <- toSingleTick e- return (Tick t e')-toSingleTick (Lam x e) = do- (e', advs) <- runWriterT (extractAdv' e)- advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,a,b')) (toSingleTick b)) advs- return (foldLets' advs' (Lam x e'))-toSingleTick (App e1 e2)- | isDelayApp e1 = do- (e2', advs) <- runWriterT (extractAdv e2)- advs' <- mapM (mapM toSingleTick) advs- return (foldLets advs' (App e1 e2'))- | otherwise = do- e1' <- toSingleTick e1- e2' <- toSingleTick e2- return (App e1' e2')--toSingleTick e@Type{} = return e-toSingleTick e@Var{} = return e-toSingleTick e@Lit{} = return e-toSingleTick e@Coercion{} = return e--foldLets :: [(Id,CoreExpr)] -> CoreExpr -> CoreExpr-foldLets ls e = foldl' (\e' (x,b) -> Let (NonRec x b) e') e ls--foldLets' :: [(Id,CoreExpr,CoreExpr)] -> CoreExpr -> CoreExpr-foldLets' ls e = foldl' (\e' (x,a,b) -> Let (NonRec x (App a b)) e') e ls--extractAdvApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr-extractAdvApp e1 e2- | isVar e2 = return (App e1 e2)- | otherwise = do- x <- lift (mkSysLocalFromExpr (fsLit "adv") e2)- tell [(x,e2)]- return (App e1 (Var x))---- removes casts and ticks from a tree-filterTree :: CoreExpr -> CoreExpr-filterTree (Cast e _) = filterTree e-filterTree (Tick _ e) = filterTree e-filterTree e = e---extractSelectApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr-extractSelectApp e1 e2- | isVar e' && isVar e2 = return (App e1 e2)- | isVar e2 = do- x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')- tell [(x, e')]- return (App (App e (Var x)) e2)- | isVar e' = do- x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)- tell [(x, e2)]- return (App e1 (Var x))- | otherwise = do- x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')- y <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)- tell [(x, e')]- tell [(y, e2)]- return (App (App e (Var x)) (Var y))- where (App e e') = filterTree e1----- This is used to pull adv out of delayed terms. The writer monad--- returns mappings from fresh variables to terms that occur as--- argument of adv.--- --- That is, occurrences of @adv t@ are replaced with @adv x@ (for some--- fresh variable @x@) and the pair @(x,t)@ is returned in the--- writer monad.-extractAdv :: CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr-extractAdv (App expr@(App e _) e2) | isSelectApp e = extractSelectApp expr e2-extractAdv e@(App e1 e2)- | isAdvApp e1 = extractAdvApp e1 e2- | isSelectApp e1 = extractSelectApp e1 e2- | isDelayApp e1 = do- (e2', advs) <- lift $ runWriterT (extractAdv e2)- advs' <- mapM (mapM extractAdv) advs- return (foldLets advs' (App e1 e2'))- | isBoxApp e1 = lift $ toSingleTick e- | otherwise = do- e1' <- extractAdv e1- e2' <- extractAdv e2- return (App e1' e2')-extractAdv (Lam x e) = do- (e', advs) <- lift $ runWriterT (extractAdv' e)- advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,b')) (extractAdvApp a b)) advs- return (foldLets advs' (Lam x e'))-extractAdv (Case e b ty alts) = do- e' <- extractAdv e- alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv f)) . getAlt) alts- return (Case e' b ty alts')-extractAdv (Cast e c) = do- e' <- extractAdv e- return (Cast e' c)-extractAdv (Tick t e) = do- e' <- extractAdv e- return (Tick t e')-extractAdv e@(Let Rec{} _) = lift $ toSingleTick e-extractAdv (Let (NonRec b e1) e2) = do- e1' <- extractAdv e1- e2' <- extractAdv e2- return (Let (NonRec b e1') e2')-extractAdv e@Type{} = return e-extractAdv e@Var{} = return e-extractAdv e@Lit{} = return e-extractAdv e@Coercion{} = return e---- This is used to pull adv out of lambdas. The writer monad returns--- mappings from fresh variables to occurrences of adv and the term it--- is applied to.--- --- That is occurrences of @adv t@ are replaced with a fresh variable--- @x@ and the triple @(x,adv,t)@ is returned in the writer monad.--- For select a b, the triple @(x, select a, b) is returned in the writer monad.-extractAdv' :: CoreExpr -> WriterT [(Id,CoreExpr,CoreExpr)] CoreM CoreExpr-extractAdv' e@(App e1 e2)- | isAdvApp e1 = do- x <- lift (mkSysLocalFromExpr (fsLit "adv") e)- tell [(x,e1,e2)]- return (Var x)- | isSelectApp e1 = do- x <- lift (mkSysLocalFromExpr (fsLit "select") e)- tell [(x,e1,e2)]- return (Var x)- | isDelayApp e1 = do- (e2', advs) <- lift $ runWriterT (extractAdv e2)- advs' <- mapM (mapM extractAdv') advs- return (foldLets advs' (App e1 e2'))- | isBoxApp e1 = lift $ toSingleTick e- | otherwise = do- e1' <- extractAdv' e1- e2' <- extractAdv' e2- return (App e1' e2')-extractAdv' (Lam x e) = do- e' <- extractAdv' e- return (Lam x e')-extractAdv' (Case e b ty alts) = do- e' <- extractAdv' e- alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv' f)) . getAlt) alts- return (Case e' b ty alts')-extractAdv' (Cast e c) = do- e' <- extractAdv' e- return (Cast e' c)-extractAdv' (Tick t e) = do- e' <- extractAdv' e- return (Tick t e')-extractAdv' e@(Let Rec{} _) = lift $ toSingleTick e-extractAdv' (Let (NonRec b e1) e2) = do- e1' <- extractAdv' e1- e2' <- extractAdv' e2- return (Let (NonRec b e1') e2')-extractAdv' e@Type{} = return e-extractAdv' e@Var{} = return e-extractAdv' e@Lit{} = return e-extractAdv' e@Coercion{} = return e----isDelayApp :: CoreExpr -> Bool-isDelayApp = isPrimApp (== "delay")--isBoxApp :: CoreExpr -> Bool-isBoxApp = isPrimApp (\occ -> occ == "Box" || occ == "box")--isAdvApp :: CoreExpr -> Bool-isAdvApp = isPrimApp (== "adv")--isSelectApp :: CoreExpr -> Bool-isSelectApp = isPrimApp (== "select")--isPrimApp :: (String -> Bool) -> CoreExpr -> Bool-isPrimApp p (App e e')- | isType e' || not (tcIsLiftedTypeKind(typeKind (exprType e'))) = isPrimApp p e- | otherwise = False-isPrimApp p (Cast e _) = isPrimApp p e-isPrimApp p (Tick _ e) = isPrimApp p e-isPrimApp p (Var v) = isPrimVar p v-isPrimApp _ _ = False--isPrimVar :: (String -> Bool) -> Var -> Bool-isPrimVar p v = maybe False id $ do- let name = varName v- mod <- nameModule_maybe name- let occ = getOccString name- return (p occ- && moduleNameString (moduleName mod) == "AsyncRattus.InternalPrimitives")
− src/AsyncRattus/Plugin/StableSolver.hs
@@ -1,83 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE CPP #-}----- | This module implements a constraint solver plugin for the--- 'Stable' type class.--module AsyncRattus.Plugin.StableSolver (tcStable) where--import AsyncRattus.Plugin.Utils- ( getNameModule, isRattModule, isStable )--import Prelude hiding ((<>))--import GHC.Plugins- (Type, Var, CommandLineOption,tyConSingleDataCon,- mkCoreConApps,getTyVar_maybe)-import GHC.Core-import GHC.Tc.Types.Evidence-import GHC.Core.Class-import GHC.Tc.Types-import GHC.Tc.Types.Constraint--import Data.Set (Set)-import qualified Data.Set as Set-#if __GLASGOW_HASKELL__ >= 904-import GHC.Types.Unique.FM-#endif------ | Constraint solver plugin for the 'Stable' type class.-tcStable :: [CommandLineOption] -> Maybe TcPlugin-tcStable _ = Just $ TcPlugin- { tcPluginInit = return ()- , tcPluginSolve = \ () -> stableSolver- , tcPluginStop = \ () -> return ()-#if __GLASGOW_HASKELL__ >= 904- , tcPluginRewrite = \ () -> emptyUFM-#endif- }---wrap :: Class -> Type -> EvTerm-wrap cls ty = EvExpr appDc- where- tyCon = classTyCon cls- dc = tyConSingleDataCon tyCon- appDc = mkCoreConApps dc [Type ty]--solveStable :: Set Var -> (Type, (Ct,Class)) -> Maybe (EvTerm, Ct)-solveStable c (ty,(ct,cl))- | isStable c ty = Just (wrap cl ty, ct)- | otherwise = Nothing--#if __GLASGOW_HASKELL__ >= 904-stableSolver :: EvBindsVar -> [Ct] -> [Ct] -> TcPluginM TcPluginSolveResult-stableSolver _ given wanted = do-#else-stableSolver :: [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult-stableSolver given _derived wanted = do-#endif-- let chSt = concatMap filterCt wanted- let haveSt = Set.fromList $ concatMap (filterTypeVar . fst) $ concatMap filterCt given- case mapM (solveStable haveSt) chSt of- Just evs -> return $ TcPluginOk evs []- Nothing -> return $ TcPluginOk [] []-- where-#if __GLASGOW_HASKELL__ >= 908- filterCt ct@(CDictCan (DictCt {di_cls = cl, di_tys = [ty]}))-#else- filterCt ct@(CDictCan {cc_class = cl, cc_tyargs = [ty]})-#endif- = case getNameModule cl of- Just (name,mod)- | isRattModule mod && name == "Stable" -> [(ty,(ct,cl))]- _ -> []- filterCt _ = []- filterTypeVar ty = case getTyVar_maybe ty of- Just v -> [v]- Nothing -> []
− src/AsyncRattus/Plugin/Strictify.hs
@@ -1,64 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE CPP #-}-module AsyncRattus.Plugin.Strictify- (checkStrictData, SCxt (..)) where-import Prelude hiding ((<>))-import Control.Monad-import AsyncRattus.Plugin.Utils--import GHC.Plugins-import GHC.Types.Tickish--data SCxt = SCxt {srcSpan :: SrcSpan}---- | Checks whether the given expression uses non-strict data types--- and issues a warning if it finds any such use.-checkStrictData :: SCxt -> CoreExpr -> CoreM ()-checkStrictData ss (Let (NonRec _ e1) e2) = - checkStrictData ss e1 >> checkStrictData ss e2-checkStrictData ss (Case e _ _ alts) = do- checkStrictData ss e- mapM_ ((\(_,_,e) -> checkStrictData ss e) . getAlt) alts-checkStrictData ss (Let (Rec es) e) = do- mapM_ (\ (_,e) -> checkStrictData ss e) es- checkStrictData ss e-checkStrictData ss (Lam _ e) = checkStrictData ss e-checkStrictData ss (Cast e _) = checkStrictData ss e-checkStrictData ss (Tick (SourceNote span _) e) = - checkStrictData (ss{srcSpan = fromRealSrcSpan span}) e-checkStrictData ss (App e1 e2)- | ignoreArgument e1 = return ()- | otherwise = do - when (not (isType e2) && tcIsLiftedTypeKind(typeKind (exprType e2))- && not (isStrict (exprType e2)) && not (isDeepseqForce e2) && not (isLit e2))- (printMessage SevWarning (srcSpan ss)- (text "The use of lazy type " <> ppr (exprType e2) <> " may lead to memory leaks. Use Control.DeepSeq.force on lazy types."))- checkStrictData ss e1- checkStrictData ss e2-checkStrictData _ss _ = return ()--isLit :: CoreExpr -> Bool-isLit Lit{} = True-isLit (App (Var v) Lit{}) - | Just (name,mod) <- getNameModule v = mod == "GHC.CString" && name == "unpackCString#"-isLit _ = False---ignoreArgument :: CoreExpr -> Bool-ignoreArgument (Var v) =- case getNameModule v of- Just (name, mod) -> - ((mod == "GHC.Exts" || mod == "GHC.IsList") && (name == "fromList" || name == "fromListN")) ||- (mod == "Data.String" && name == "fromString") ||- (mod == "GHC.Stack.Types" && name == "pushCallStack") ||- (mod == "Data.Text.Internal" && name == "pack")- _ -> False-ignoreArgument (App x _) = ignoreArgument x-ignoreArgument _ = False--isDeepseqForce :: CoreExpr -> Bool-isDeepseqForce (App (App (App (Var v) _) _) _) =- case getNameModule v of- Just (name, mod) -> mod == "Control.DeepSeq" && name == "force"- _ -> False-isDeepseqForce _ = False
− src/AsyncRattus/Plugin/Transform.hs
@@ -1,143 +0,0 @@-{-# LANGUAGE TupleSections #-}-module AsyncRattus.Plugin.Transform (- transform-) where--import GHC.Core.Opt.Monad-import GHC.Plugins-import AsyncRattus.Plugin.PrimExpr-import AsyncRattus.Plugin.Utils-import Data.Maybe (fromJust)-import Prelude hiding ((<>))-import Data.Functor ((<&>))-import Control.Applicative ((<|>))-import Data.Tuple (swap)--data Ctx = Ctx {- fresh :: Maybe Var-}--emptyCtx :: Ctx-emptyCtx = Ctx {- fresh = Nothing-}--replaceVar :: Var -> Var -> Expr Var -> Expr Var-replaceVar match rep (Var v) = if v == match then Var rep else Var v-replaceVar match rep (App e e') = App (replaceVar match rep e) (replaceVar match rep e')-replaceVar match rep (Tick _ e) = replaceVar match rep e-replaceVar match rep (Lam v e) = Lam (if v == match then rep else v) (replaceVar match rep e)-replaceVar match rep (Let (NonRec b e') e) =- Let (NonRec newB (replaceVar match rep e')) (replaceVar match rep e)- where newB = if b == match then rep else b-replaceVar match rep (Cast e _) = replaceVar match rep e-replaceVar match rep (Case e b t alts) =- Case newExpr newB t (map (\(Alt con binds expr) -> Alt con (map (\v -> if v == match then rep else v) binds) (replaceVar match rep expr)) alts)- where newExpr = replaceVar match rep e- newB = if b == match then rep else b-replaceVar _ _ e = e--transformPrim :: Ctx -> Expr Var -> CoreM (Expr Var, PrimInfo)-transformPrim ctx expr@(App e e') = case isPrimExpr expr of- Just primInfo@(AdvApp f _) -> do- varAdv' <- adv'Var- let newE = replaceVar f varAdv' e- return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)- Just primInfo@(SelectApp f _ _) -> do- varSelect' <- select'Var- let newE = replaceVar f varSelect' e- return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)- Just (DelayApp _ t) -> do- bigDelayVar <- bigDelay- inputValueV <- inputValueVar- let inputValueType = mkTyConTy inputValueV - inpVar <- mkSysLocalM (fsLit "inpV") inputValueType inputValueType- let ctx' = ctx {fresh = Just inpVar}- (newExpr, maybePrimInfo) <- transform' ctx' e'- let primInfo = fromJust maybePrimInfo- let lambdaExpr = Lam inpVar newExpr- clockCode <- constructClockExtractionCode primInfo- return (App (App (App (Var bigDelayVar) (Type t)) clockCode) lambdaExpr, primInfo)- Just primInfo -> do- error $ showSDocUnsafe $ text "transformPrim: Cannot transform " <> ppr (prim primInfo)- Nothing -> error "Cannot transform non-prim applications"-transformPrim _ _ = do- error "Cannot transform anything else than prim applications"---transform :: CoreExpr -> CoreM CoreExpr-transform expr = fst <$> transform' emptyCtx expr--transform' :: Ctx -> CoreExpr -> CoreM (CoreExpr, Maybe PrimInfo)-transform' ctx expr@(App e e') = case isPrimExpr expr of- Just (BoxApp _) -> do- (newExpr, primInfo) <- transform' ctx e'- return (App e newExpr, primInfo)- (Just _) -> do- (newExpr, primInfo) <- transformPrim ctx expr- return (newExpr, Just primInfo)- Nothing -> do- (newExpr, primInfo) <- transform' ctx e- (newExpr', primInfo') <- transform' ctx e'- return (App newExpr newExpr', primInfo <|> primInfo')-transform' ctx (Lam b rhs) = do- (newExpr, primInfo) <- transform' ctx rhs- return (Lam b newExpr, primInfo)-transform' ctx (Let (NonRec b rhs) e) = do- (newRhs, primInfo) <- transform' ctx rhs- (newExpr, primInfo') <- transform' ctx e- return (Let (NonRec b newRhs) newExpr, primInfo <|> primInfo')-transform' ctx (Let (Rec binds) e) = do- transformedBinds <- mapM (\(b, bindE) -> fmap (b,) (transform' ctx bindE)) binds- (e', mPi) <- transform' ctx e- let primInfos = map (\(_, (_, p)) -> p) transformedBinds- let firstPrimInfo = foldl (<|>) mPi primInfos- newBinds <- mapM (\(b, (e, _)) -> return (b, e)) transformedBinds- return (Let (Rec newBinds) e', firstPrimInfo)-transform' ctx (Case e b t alts) = do- -- The checking pass has ensured that there are not advances on different- -- clocks. Thus we can just pick the first PrimInfo we find.- (expr, primInfo) <- transform' ctx e-- -- For each alternative, transform it and save the maybePrimInfo-value- transformed <- mapM (\(Alt con binds expr) -> transform' ctx expr <&> fmap (Alt con binds) . swap) alts-- -- Of all the primInfos we have, pick the first one. This is safe because- -- the checking pass has ensured that the clocks of all primitives.- let firstPrimInfo = foldl (\acc (p, _) -> acc <|> p) primInfo transformed- let alts' = map snd transformed- return (Case expr b t alts', firstPrimInfo)-transform' ctx (Cast e c) = do (e' , p) <- transform' ctx e; return (Cast e' c, p)-transform' ctx (Tick t e) = do (e' , p) <- transform' ctx e; return (Tick t e', p)-transform' _ e = return (e, Nothing)--constructClockExtractionCode :: PrimInfo -> CoreM CoreExpr-constructClockExtractionCode (AdvApp _ arg) = createClockCode arg-constructClockExtractionCode (SelectApp _ arg arg2) = clockUnion arg arg2-constructClockExtractionCode primInfo = error $ "Cannot construct clock for prim " ++ showSDocUnsafe (ppr (prim primInfo))---createClockCode :: (Var, Type) -> CoreM CoreExpr-createClockCode (argV, argT) = do- extractClock <- extractClockVar- return $ App (App (Var extractClock) (Type argT)) (Var argV)---- Generate code for union of two clocks.--- clockUnion (aVar, aType) (bVar, bType) returns the AST for:--- Set.union (extractClock aVar) (extractClock bVar)--clockUnion :: (Var,Type) -> (Var, Type) -> CoreM CoreExpr-clockUnion arg arg2 = do- clock1Code <- createClockCode arg- clock2Code <- createClockCode arg2- unionVar' <- unionVar- return $- App- (- App- (- (Var unionVar')- )- clock1Code- )- clock2Code
− src/AsyncRattus/Plugin/Utils.hs
@@ -1,380 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE CPP #-}--module AsyncRattus.Plugin.Utils (- printMessage,- Severity(..),- isRattModule,- adv'Var,- select'Var,- bigDelay,- inputValueVar,- extractClockVar,- unionVar,- isGhcModule,- getNameModule,- isStable,- isStrict,- isTemporal,- userFunction,- typeClassFunction,- getVar,- getMaybeVar,- getModuleFS,- isVar,- isType,- mkSysLocalFromVar,- mkSysLocalFromExpr,- fromRealSrcSpan,- noLocationInfo,- mkAlt,- getAlt,- splitForAllTys')- where--#if __GLASGOW_HASKELL__ >= 908-import GHC.Types.Error (ResolvedDiagnosticReason (..))-#endif--#if __GLASGOW_HASKELL__ >= 906-import GHC.Builtin.Types.Prim-import GHC.Tc.Utils.TcType-#endif-#if __GLASGOW_HASKELL__ >= 904-import qualified GHC.Data.Strict as Strict-import Control.Concurrent.MVar (readMVar)-#else-import Data.IORef (readIORef)-#endif -import GHC.Utils.Logger-import GHC.Plugins-import GHC.Utils.Error hiding (errorMsg)-import GHC.Utils.Monad---import GHC.Types.Name.Cache (NameCache(nsNames), lookupOrigNameCache, OrigNameCache)-import qualified GHC.Types.Name.Occurrence as Occurrence-import GHC.Types.TyThing--import Prelude hiding ((<>))-import Data.Set (Set)-import qualified Data.Set as Set-import Data.Char-import Data.Maybe---getMaybeVar :: CoreExpr -> Maybe Var-getMaybeVar (App e e')- | isType e' || not (tcIsLiftedTypeKind (typeKind (exprType e'))) = getMaybeVar e- | otherwise = Nothing-getMaybeVar (Cast e _) = getMaybeVar e-getMaybeVar (Tick _ e) = getMaybeVar e-getMaybeVar (Var v) = Just v-getMaybeVar _ = Nothing--getVar :: CoreExpr -> Var-getVar = fromJust . getMaybeVar--isVar :: CoreExpr -> Bool-isVar = isJust . getMaybeVar--isType Type {} = True-isType (App e _) = isType e-isType (Cast e _) = isType e-isType (Tick _ e) = isType e-isType _ = False--#if __GLASGOW_HASKELL__ >= 906-isFunTyCon = isArrowTyCon-repSplitAppTys = splitAppTysNoView-#endif- --printMessage :: (HasDynFlags m, MonadIO m, HasLogger m) =>- Severity -> SrcSpan -> SDoc -> m ()-printMessage sev loc doc = do-#if __GLASGOW_HASKELL__ >= 908- logger <- getLogger- liftIO $ putLogMsg logger (logFlags logger)- (MCDiagnostic sev (if sev == SevError then ResolvedDiagnosticReason ErrorWithoutFlag else ResolvedDiagnosticReason WarningWithoutFlag) Nothing) loc doc-#elif __GLASGOW_HASKELL__ >= 906- logger <- getLogger- liftIO $ putLogMsg logger (logFlags logger)- (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag) Nothing) loc doc-#elif __GLASGOW_HASKELL__ >= 904- logger <- getLogger- liftIO $ putLogMsg logger (logFlags logger)- (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag)) loc doc-#else- dflags <- getDynFlags- logger <- getLogger- liftIO $ putLogMsg logger dflags NoReason sev loc doc-#endif--instance Ord FastString where- compare = uniqCompareFS--{--******************************************************-* Extracting variables *-******************************************************--}---origNameCache :: CoreM OrigNameCache-origNameCache = do- hscEnv <- getHscEnv-#if __GLASGOW_HASKELL__ >= 904- let nameCache = hsc_NC hscEnv- liftIO $ readMVar (nsNames nameCache)-#else- nameCache <- liftIO $ readIORef (hsc_NC hscEnv)- return $ nsNames nameCache-#endif---getNamedThingFromModuleAndOccName :: String -> OccName -> CoreM TyThing-getNamedThingFromModuleAndOccName moduleName occName = do- origNameCache <- origNameCache- case filter ((moduleName ==) . unpackFS . getModuleFS) (moduleEnvKeys origNameCache) of- mod : _ -> lookupThing $ fromJust $ lookupOrigNameCache origNameCache mod occName- _ -> error ( ("internal error: cannot find module " ++ moduleName ++ "; " ++ show (map (unpackFS . getModuleFS) $ moduleEnvKeys origNameCache)))--getVarFromModule :: String -> String -> CoreM Var-getVarFromModule moduleName = fmap tyThingId . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.varName--getTyConFromModule :: String -> String -> CoreM TyCon-getTyConFromModule moduleName = fmap tyThingTyCon . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.tcName--adv'Var :: CoreM Var-adv'Var = getVarFromModule "AsyncRattus.InternalPrimitives" "adv'"--select'Var :: CoreM Var-select'Var = getVarFromModule "AsyncRattus.InternalPrimitives" "select'"--bigDelay :: CoreM Var-bigDelay = getVarFromModule "AsyncRattus.InternalPrimitives" "Delay"--inputValueVar :: CoreM TyCon-inputValueVar = getTyConFromModule "AsyncRattus.InternalPrimitives" "InputValue"--extractClockVar :: CoreM Var-extractClockVar = getVarFromModule "AsyncRattus.InternalPrimitives" "extractClock"--unionVar :: CoreM Var-unionVar = getVarFromModule "AsyncRattus.InternalPrimitives" "clockUnion"--rattModules :: Set FastString-rattModules = Set.fromList ["AsyncRattus.InternalPrimitives","AsyncRattus.Channels"]--getModuleFS :: Module -> FastString-getModuleFS = moduleNameFS . moduleName--isRattModule :: FastString -> Bool-isRattModule = (`Set.member` rattModules)--isGhcModule :: FastString -> Bool-isGhcModule = (== "GHC.Types")--getNameModule :: NamedThing a => a -> Maybe (FastString, FastString)-getNameModule v = do- let name = getName v- mod <- nameModule_maybe name- return (getOccFS name,moduleNameFS (moduleName mod))----- | The set of stable built-in types.-ghcStableTypes :: Set FastString-ghcStableTypes = Set.fromList ["Word","Int","Bool","Float","Double","Char", "IO"]--isGhcStableType :: FastString -> Bool-isGhcStableType = (`Set.member` ghcStableTypes)---newtype TypeCmp = TC Type--instance Eq TypeCmp where- (TC t1) == (TC t2) = eqType t1 t2--instance Ord TypeCmp where- compare (TC t1) (TC t2) = nonDetCmpType t1 t2--isTemporal :: Type -> Bool-isTemporal t = isTemporalRec 0 Set.empty t---isTemporalRec :: Int -> Set TypeCmp -> Type -> Bool-isTemporalRec d _ _ | d == 100 = False-isTemporalRec _ pr t | Set.member (TC t) pr = False-isTemporalRec d pr t = do- let pr' = Set.insert (TC t) pr- case splitTyConApp_maybe t of- Nothing -> False- Just (con,args) ->- case getNameModule con of- Nothing -> False- Just (name,mod)- -- If it's a Rattus type constructor check if it's a box- | isRattModule mod && (name == "Box" || name == "O") -> True- | isFunTyCon con -> or (map (isTemporalRec (d+1) pr') args)- | isAlgTyCon con ->- case algTyConRhs con of- DataTyCon {data_cons = cons} -> or (map check cons)- where check con = case dataConInstSig con args of- (_, _,tys) -> or (map (isTemporalRec (d+1) pr') tys)- _ -> or (map (isTemporalRec (d+1) pr') args)- _ -> False----- | Check whether the given type is stable. This check may use--- 'Stable' constraints from the context.--isStable :: Set Var -> Type -> Bool-isStable c t = isStableRec c 0 Set.empty t---- | Check whether the given type is stable. This check may use--- 'Stable' constraints from the context.--isStableRec :: Set Var -> Int -> Set TypeCmp -> Type -> Bool--- To prevent infinite recursion (when checking recursive types) we--- keep track of previously checked types. This, however, is not--- enough for non-regular data types. Hence we also have a counter.-isStableRec _ d _ _ | d == 100 = True-isStableRec _ _ pr t | Set.member (TC t) pr = True-isStableRec c d pr t = do- let pr' = Set.insert (TC t) pr- case splitTyConApp_maybe t of- Nothing -> case getTyVar_maybe t of- Just v -> -- if it's a type variable, check the context- v `Set.member` c- Nothing -> False- Just (con,args) ->- case getNameModule con of- Nothing -> False- Just (name,mod)- | mod == "GHC.Num.Integer" && name == "Integer" -> True- | mod == "Data.Text.Internal" && name == "Text" -> True- -- If it's a Rattus type constructor check if it's a box- | isRattModule mod && (name == "Box" || name == "Chan") -> True- -- If its a built-in type check the set of stable built-in types- | isGhcModule mod -> isGhcStableType name- {- deal with type synonyms (does not seem to be necessary (??))- | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->- isStableRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}- | isAlgTyCon con ->- case algTyConRhs con of- DataTyCon {data_cons = cons, is_enum = enum}- | enum -> True- | all hasStrictArgs cons ->- and (map check cons)- | otherwise -> False- where check con = case dataConInstSig con args of- (_, _,tys) -> and (map (isStableRec c (d+1) pr') tys)- TupleTyCon {} -> null args- _ -> False- _ -> False----isStrict :: Type -> Bool-isStrict t = isStrictRec 0 Set.empty t--splitForAllTys' :: Type -> ([TyCoVar], Type)-splitForAllTys' = splitForAllTyCoVars---- | Check whether the given type is stable. This check may use--- 'Stable' constraints from the context.--isStrictRec :: Int -> Set TypeCmp -> Type -> Bool--- To prevent infinite recursion (when checking recursive types) we--- keep track of previously checked types. This, however, is not--- enough for non-regular data types. Hence we also have a counter.-isStrictRec d _ _ | d == 100 = True-isStrictRec _ pr t | Set.member (TC t) pr = True-isStrictRec d pr t = do- let pr' = Set.insert (TC t) pr- let (_,t') = splitForAllTys' t- let (c, tys) = repSplitAppTys t'- if isJust (getTyVar_maybe c) then and (map (isStrictRec (d+1) pr') tys)- else case splitTyConApp_maybe t' of- Nothing -> isJust (getTyVar_maybe t)- Just (con,args) ->- case getNameModule con of- Nothing -> False- Just (name,mod)- | mod == "GHC.Num.Integer" && name == "Integer" -> True- | mod == "Data.Text.Internal" && name == "Text" -> True- | mod == "GHC.IORef" && name == "IORef" -> True- | mod == "GHC.MVar" && name == "MVar" -> True- -- If it's a Rattus type constructor check if it's a box- | isRattModule mod && (name == "Box" || name == "Chan" || name == "O" || name == "Output") -> True- -- If its a built-in type check the set of stable built-in types- | isGhcModule mod -> isGhcStableType name- {- deal with type synonyms (does not seem to be necessary (??))- | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->- isStrictRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}- | isFunTyCon con -> True- | isAlgTyCon con ->- case algTyConRhs con of- DataTyCon {data_cons = cons, is_enum = enum}- | enum -> True- | all hasStrictArgs cons ->- and (map check cons)- | otherwise -> False- where check con = case dataConInstSig con args of- (_, _,tys) -> and (map (isStrictRec (d+1) pr') tys)- TupleTyCon {} -> null args- NewTyCon {nt_rhs = ty} -> isStrictRec (d+1) pr' ty- _ -> False- | otherwise -> False------hasStrictArgs :: DataCon -> Bool-hasStrictArgs con = all isBanged (dataConImplBangs con)--userFunction :: Var -> Bool-userFunction v- | typeClassFunction v = True- | otherwise = - case getOccString (getName v) of- (c : _)- | isUpper c || c == '$' || c == ':' -> False- | otherwise -> True- _ -> False--typeClassFunction :: Var -> Bool-typeClassFunction v =- case getOccString (getName v) of- ('$' : 'c' : _) -> True- ('$' : 'f' : _) -> True- _ -> False--mkSysLocalFromVar :: MonadUnique m => FastString -> Var -> m Id-mkSysLocalFromVar lit v = mkSysLocalM lit (varMult v) (varType v)- -mkSysLocalFromExpr :: MonadUnique m => FastString -> CoreExpr -> m Id-mkSysLocalFromExpr lit e = mkSysLocalM lit oneDataConTy (exprType e)- - -fromRealSrcSpan :: RealSrcSpan -> SrcSpan-#if __GLASGOW_HASKELL__ >= 904-fromRealSrcSpan span = RealSrcSpan span Strict.Nothing-#else-fromRealSrcSpan span = RealSrcSpan span Nothing-#endif--instance Ord SrcSpan where- compare (RealSrcSpan s _) (RealSrcSpan t _) = compare s t- compare RealSrcSpan{} _ = LT- compare _ _ = GT--noLocationInfo :: SrcSpan-noLocationInfo = UnhelpfulSpan UnhelpfulNoLocationInfo--mkAlt c args e = Alt c args e-getAlt (Alt c args e) = (c, args, e)
− src/AsyncRattus/Primitives.hs
@@ -1,21 +0,0 @@--- | The language primitives of Async Rattus. Note that the typing--- rules for 'delay', 'adv','select' and 'box' are more restrictive--- than the Haskell types that are indicated. The stricter Async--- Rattus typing rules for these primitives are given below.--{-# LANGUAGE TypeOperators #-}-module AsyncRattus.Primitives- (O- ,Box- ,Select(..)- ,delay- ,adv- ,promote- ,box- ,unbox- ,select- ,never- ,Stable- ,Continuous- ) where-import AsyncRattus.InternalPrimitives
− src/AsyncRattus/Signal.hs
@@ -1,412 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeFamilies #-}-{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE CPP #-}----- | Programming with signals.--module AsyncRattus.Signal- ( map- , mkInputSig- , getInputSig- , filterMap- , filterMapAwait- , filter- , filterAwait- , trigger- , triggerAwait- , mapAwait- , switch- , switchS- , switchAwait- , interleave- , mkSig- , mkBoxSig- , current- , future- , const- , scan- , scanC- , scanAwait- , scanAwaitC- , scanMap- , Sig(..)- , zipWith- , zipWith3- , zip- , cond- , update- , integral- , derivative- )--where--import AsyncRattus-import AsyncRattus.Channels-import Prelude hiding (map, const, zipWith, zipWith3, zip, filter)-import Data.VectorSpace-import Data.Ratio ((%))--- TODO: InternalPrimitives is only used to implment instance of--- Continuous. Replace this manual instance declaration with Template--- Haskell.-import AsyncRattus.InternalPrimitives--infixr 5 :::---- | @Sig a@ is a stream of values of type @a@.-data Sig a = !a ::: !(O (Sig a))--instance Producer (Sig a) a where- getCurrent p = Just' (current p)- getNext p cb = cb (future p)--newtype SigMaybe a = SigMaybe (Sig (Maybe' a))--instance Producer (SigMaybe a) a where- getCurrent (SigMaybe p) = current p- getNext (SigMaybe p) cb = cb (delay (SigMaybe (adv (future p))))---- | Get the current value of a signal.-current :: Sig a -> a-current (x ::: _) = x----- | Get the future the signal.-future :: Sig a -> O (Sig a)-future (_ ::: xs) = xs---- | Apply a function to the value of a signal.-map :: Box (a -> b) -> Sig a -> Sig b-map f (x ::: xs) = unbox f x ::: delay (map f (adv xs))---- | Variant of 'getInput' that returns a signal instead of a boxed--- delayed computation.-getInputSig :: IO (Box (O (Sig a)) :* (a -> IO ()))-getInputSig = do (s :* cb) <- getInput- return (mkBoxSig s :* cb)---- | Turn a producer into a signal. This is a variant of 'mkInput'--- that returns a signal instead of a boxed delayed computation.-mkInputSig :: Producer p a => p -> IO (Box (O (Sig a)))-mkInputSig p = mkBoxSig <$> mkInput p----- | This function is essentially the composition of 'filter' with--- 'map'. The signal produced by @filterMap f s@ has the value @v@--- whenever @s@ has the value @u@ such that @unbox f u = Just' v@.-filterMap :: Box (a -> Maybe' b) -> Sig a -> IO (Box (O (Sig b)))-filterMap f s = mkInputSig (SigMaybe (map f s))---- | This function is similar to 'filterMap' but takes a delayed--- signal (type @O (Sig a)@) as an argument instead of a signal (@Sig--- a@).-filterMapAwait :: Box (a -> Maybe' b) -> O (Sig a) -> IO (Box (O (Sig b)))-filterMapAwait f s = mkInputSig (delay (SigMaybe (map f (adv s))))---- | Filter the given signal using a predicate. The signal produced by--- @filter p s@ contains only values from @s@ that satisfy the--- predicate @p@.-filter :: Box (a -> Bool) -> Sig a -> IO (Box (O (Sig a)))-filter p = filterMap (box (\ x -> if unbox p x then Just' x else Nothing'))---- | This function is similar to 'filter' but takes a delayed signal--- (type @O (Sig a)@) as an argument instead of a signal (@Sig a@).-filterAwait :: Box (a -> Bool) -> O (Sig a) -> IO (Box (O (Sig a)))-filterAwait p = filterMapAwait (box (\ x -> if unbox p x then Just' x else Nothing'))----- | This function is a variant of 'zipWith'. Whereas @zipWith f xs--- ys@ produces a new value whenever @xs@ or @ys@ produce a new value,--- @trigger f xs ys@ only produces a new value when xs produces a new--- value.------ Example:------ > xs: 1 2 3 2--- > ys: 1 0 5 2--- >--- > zipWith (box (+)) xs ys: 2 3 4 3 8 4--- > trigger (box (+)) xy ys: 2 3 8 4--trigger :: (Stable a, Stable b) => Box (a -> b -> c) -> Sig a -> Sig b -> IO (Box (Sig c))-trigger f (a ::: as) bs@(b:::_) = do s <- triggerAwait f as bs- return (box (unbox f a b ::: unbox s))--- | This function is similar to 'trigger' but takes a delayed signal--- (type @O (Sig a)@) as an argument instead of a signal (@Sig a@).-triggerAwait :: Stable b => Box (a -> b -> c) -> O (Sig a) -> Sig b -> IO (Box (O (Sig c)))-triggerAwait f as bs = mkBoxSig <$> mkInput (box SigMaybe `mapO` (trig f as bs)) where- trig :: Stable b => Box (a -> b -> c) -> O (Sig a) -> Sig b -> O (Sig (Maybe' c))- trig f as (b ::: bs) =- delay (case select as bs of- Fst (a' ::: as') bs' -> Just' (unbox f a' b) ::: trig f as' (b ::: bs')- Snd as' bs' -> Nothing' ::: trig f as' bs'- Both (a' ::: as') (b' ::: bs') -> Just' (unbox f a' b') ::: trig f as' (b' ::: bs')- )---- | A version of @map@ for delayed signals.-mapAwait :: Box (a -> b) -> O (Sig a) -> O (Sig b)-mapAwait f d = delay (map f (adv d))---- | Turns a boxed delayed computation into a delayed signal.-mkSig :: Box (O a) -> O (Sig a)-mkSig b = delay (adv (unbox b) ::: mkSig b)---- | Variant of 'mkSig' that returns a boxed delayed signal-mkBoxSig :: Box (O a) -> Box (O (Sig a))-mkBoxSig b = box (mkSig b)----- | Construct a constant signal that never updates.-const :: a -> Sig a-const x = x ::: never---- | Similar to Haskell's 'scanl'.------ > scan (box f) x (v1 ::: v2 ::: v3 ::: ... ) == (x `f` v1) ::: ((x `f` v1) `f` v2) ::: ...------ Note: Unlike 'scanl', 'scan' starts with @x `f` v1@, not @x@.--scan :: (Continuous b) => Box(b -> a -> b) -> b -> Sig a -> Sig b-scan f acc (a ::: as) = acc' ::: delay (scan f (unbox accBox) (adv as))- where acc' = unbox f acc a- accBox = promote acc'--scanC :: (Continuous b) => Box(b -> a -> C b) -> b -> Sig a -> C (Sig b)-scanC f acc (a ::: as) = do- acc' <- unbox f acc a- let accBox = promote acc'- fut <- delayC $ delay (scanC f (unbox accBox) (adv as))- return (acc' ::: fut)- where - --- | Like 'scan', but uses a delayed signal.-scanAwait :: (Continuous b) => Box (b -> a -> b) -> b -> O (Sig a) -> Sig b-scanAwait f acc as = acc ::: delay (scan f (unbox accBox) (adv as))- where accBox = promote acc --scanAwaitC :: (Continuous b) => Box (b -> a -> C b) -> b -> O (Sig a) -> C (Sig b)-scanAwaitC f acc as = do - fut <- delayC $ delay (scanC f (unbox accBox) (adv as))- return (acc ::: fut)- where accBox = promote acc ---- | 'scanMap' is a composition of 'map' and 'scan':------ > scanMap f g x === map g . scan f x-scanMap :: (Continuous b) => Box (b -> a -> b) -> Box (b -> c) -> b -> Sig a -> Sig c-scanMap f p acc (a ::: as) = unbox p acc' ::: delay (scanMap f p (unbox accBox) (adv as))- where acc' = unbox f acc a- accBox = promote acc'---- | This function allows to switch from one signal to another one--- dynamically. The signal defined by @switch xs ys@ first behaves--- like @xs@, but as soon as @ys@ produces a new value, @switch xs ys@--- behaves like @ys@.------ Example:------ > xs: 1 2 3 4 5 6 7 8 9--- > ys: 1 2 3 4 5 6--- >--- > switch xs ys: 1 2 3 1 2 4 3 4 5 6-switch :: Sig a -> O (Sig a) -> Sig a-switch (x ::: xs) d = x ::: delay (case select xs d of- Fst xs' d' -> switch xs' d'- Snd _ d' -> d'- Both _ d' -> d')---- | This function is similar to 'switch', but the (future) second--- signal may depend on the last value of the first signal.-switchS :: Continuous a => Sig a -> O (a -> Sig a) -> Sig a-switchS (x ::: xs) d = x ::: delay (case select xs d of- Fst xs' d' -> switchS xs' d'- Snd _ f -> f (unbox xBox)- Both _ f -> f (unbox xBox))- where xBox = promote x---- | This function is similar to 'switch' but works on delayed signals--- instead of signals.-switchAwait :: O (Sig a) -> O (Sig a) -> O (Sig a)-switchAwait xs ys = delay (case select xs ys of- Fst xs' d' -> switch xs' d'- Snd _ d' -> d'- Both _ d' -> d')---- | This function interleaves two signals producing a new value @v@--- whenever either input stream produces a new value @v@. In case the--- input signals produce a new value simultaneously, the function--- argument is used break ties, i.e. to compute the new output value based--- on the two new input values------ Example:------ > xs: 1 3 5 3 1 3--- > ys: 0 2 4--- >--- > interleave (box (+)) xs ys: 1 3 2 5 7 1 3-interleave :: Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)-interleave f xs ys = delay (case select xs ys of- Fst (x ::: xs') ys' -> x ::: interleave f xs' ys'- Snd xs' (y ::: ys') -> y ::: interleave f xs' ys'- Both (x ::: xs') (y ::: ys') -> unbox f x y ::: interleave f xs' ys')----- | Takes two signals and updates the first signal using the--- functions produced by the second signal:------ Law:------ (xs `update` fs) `update` gs = (xs `update` (interleave (box (.)) gs fs))-update :: (Continuous a) => Sig a -> O (Sig (a -> a)) -> Sig a-update (x ::: xs) fs = x ::: delay - (case select xs fs of- Fst xs' ys' -> update xs' ys'- Snd xs' (f ::: fs') -> update (f (unbox xBox) ::: xs') fs'- Both (x' ::: xs') (f ::: fs') -> update (f x' ::: xs') fs')- where xBox = promote x----- | This function is a variant of combines the values of two signals--- using the function argument. @zipWith f xs ys@ produces a new value--- @unbox f x y@ whenever @xs@ or @ys@ produce a new value, where @x@--- and @y@ are the current values of @xs@ and @ys@, respectively.------ Example:------ > xs: 1 2 3 2--- > ys: 1 0 5 2--- >--- > zipWith (box (+)) xs ys: 2 3 4 3 8 4--zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> Sig a -> Sig b -> Sig c-zipWith f (a ::: as) (b ::: bs) = unbox f a b ::: delay (- case select as bs of- Fst as' lbs -> zipWith f as' (b ::: lbs)- Snd las bs' -> zipWith f (a ::: las) bs'- Both as' bs' -> zipWith f as' bs'- )---- | Variant of 'zipWith' with three signals.-zipWith3 :: forall a b c d. (Stable a, Stable b, Stable c) => Box(a -> b -> c -> d) -> Sig a -> Sig b -> Sig c -> Sig d-zipWith3 f as bs cs = zipWith (box (\f x -> unbox f x)) cds cs- where cds :: Sig (Box (c -> d))- cds = zipWith (box (\a b -> box (\ c -> unbox f a b c))) as bs---- | If-then-else lifted to signals. @cond bs xs ys@ produces a stream--- whose value is taken from @xs@ whenever @bs@ is true and from @ys@--- otherwise.-cond :: Stable a => Sig Bool -> Sig a -> Sig a -> Sig a-cond = zipWith3 (box (\b x y -> if b then x else y))----- | This is a special case of 'zipWith' using the tupling--- function. That is,------ > zip = zipWith (box (:*))-zip :: (Stable a, Stable b) => Sig a -> Sig b -> Sig (a:*b)-zip = zipWith (box (:*))---- | Sampling interval (in microseconds) for the 'integral' and--- 'derivative' functions.--dt :: Int-dt = 20000---- | @integral x xs@ computes the integral of the signal @xs@ with the--- constant @x@. For example, if @xs@ is the velocity of an object,--- the signal @integral 0 xs@ describes the distance travelled by that--- object.-integral :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)- => v -> Sig v -> Sig v-integral = int - where int cur (x ::: xs)- | x == zeroVector = cur ::: delay (int cur (adv xs))- | otherwise = cur ::: delay (- case select xs (unbox (timer dt)) of- Fst xs' _ -> int cur xs'- Snd xs' _ -> int (dtf *^ (cur ^+^ x)) (x ::: xs')- Both (x' ::: xs') _ -> int (dtf *^ (cur ^+^ x')) (x'::: xs'))- -- sampling interval in seconds- dtf :: a- dtf = fromRational (fromIntegral dt % 1000000)- --- | Compute the derivative of a signal. For example, if @xs@ is the--- velocity of an object, the signal @derivative xs@ describes the--- acceleration travelled by that object.-derivative :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)- => Sig v -> Sig v-derivative xs = der zeroVector (current xs) xs where- -- inverse sampling interval in seconds- dtf :: a- dtf = fromIntegral dt * 0.000001-- der :: v -> v -> Sig v -> Sig v- der d last (x:::xs)- | d == zeroVector = zeroVector ::: delay- (let x' ::: xs' = adv xs- in der ((x' ^-^ x) ^/ dtf) x (x' ::: xs'))- | otherwise = d ::: delay (- case select xs (unbox (timer dt)) of- Fst xs' _ -> der d last xs'- Snd xs' _ -> der ((x ^-^ last) ^/ dtf) x (x ::: xs')- Both (x' ::: xs') _ -> der ((x' ^-^ last) ^/ dtf) x' (x' ::: xs'))---instance Continuous a => Continuous (Sig a) where- progressInternal inp (x ::: xs@(Delay cl _)) = - if inputInClock inp cl then (adv' xs inp)- else progressInternal inp x ::: xs- progressAndNext inp (x ::: xs@(Delay cl _)) = - if inputInClock inp cl then let n = adv' xs inp in (n, nextProgress n)- else let (n , cl') = progressAndNext inp x in (n ::: xs , cl `clockUnion` cl')- nextProgress (x ::: (Delay cl _)) = nextProgress x `clockUnion` cl---- Prevent functions from being inlined too early for the rewrite--- rules to fire.--{-# NOINLINE [1] map #-}-{-# NOINLINE [1] const #-}-{-# NOINLINE [1] scan #-}-{-# NOINLINE [1] scanMap #-}-{-# NOINLINE [1] zip #-}-{-# NOINLINE [1] update #-}-{-# NOINLINE [1] switch #-}---{-# RULES-- "const/switch" forall x xs.- switch (const x) xs = x ::: xs;-- "update/update" forall xs fs gs.- update (update xs fs) gs = update xs (interleave (box (.)) gs fs) ;-- "const/map" forall (f :: Stable b => Box (a -> b)) x.- map f (const x) = let x' = unbox f x in const x' ;-- "map/map" forall f g xs.- map f (map g xs) = map (box (unbox f . unbox g)) xs ;-- "map/scan" forall f p acc as.- map p (scan f acc as) = scanMap f p acc as ;-- "zip/map" forall xs ys f.- map f (zip xs ys) = let f' = unbox f in zipWith (box (\ x y -> f' (x :* y))) xs ys;-- "scan/scan" forall f g b c as.- scan g c (scan f b as) =- let f' = unbox f; g' = unbox g in- scanMap (box (\ (b:*c) a -> let b' = f' b a in (b':* g' c b'))) (box snd') (b:*c) as ;-- "scan/scanMap" forall f g p b c as.- scan g c (scanMap f p b as) =- let f' = unbox f; g' = unbox g; p' = unbox p in- scanMap (box (\ (b:*c) a -> let b' = f' (p' b) a in (b':* g' c b'))) (box snd') (b:*c) as ;--#-}-
− src/AsyncRattus/Strict.hs
@@ -1,255 +0,0 @@-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE TypeOperators #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE TemplateHaskell #-}----- | This module contains strict versions of some standard data--- structures.----module AsyncRattus.Strict- ( List(..),- singleton,- IsList(..),- init',- reverse',- union',- unionBy',- nub',- nubBy',- filter',- delete',- deleteBy',- (+++),- listToMaybe',- map',- zip',- zipWith',- mapMaybe',- (:*)(..),- Maybe'(..),- maybe',- fromMaybe',- fst',- snd',- curry',- uncurry'- )where--import Prelude hiding (map)-import Data.VectorSpace-import AsyncRattus.Derive-import GHC.Exts (IsList(..))--infixr 2 :*--- | Strict pair type.-data a :* b = !a :* !b--continuous ''(:*)---- | First projection function.-fst' :: (a :* b) -> a-fst' (a:*_) = a---- | Second projection function.-snd' :: (a :* b) -> b-snd' (_:*b) = b--curry' :: ((a :* b) -> c) -> a -> b -> c-curry' f x y = f (x :* y)--uncurry' :: (a -> b -> c) -> (a :* b) -> c-uncurry' f (x :* y) = f x y---instance Functor ((:*) a) where- fmap f (x:*y) = (x :* f y)- -instance (Show a, Show b) => Show (a:*b) where- show (a :* b) = "(" ++ show a ++ " :* " ++ show b ++ ")"--instance (Eq a, Eq b) => Eq (a :* b) where- (x1 :* y1) == (x2 :* y2) = x1 == x2 && y1 == y2---instance (VectorSpace v a, VectorSpace w a, Floating a, Eq a) => VectorSpace (v :* w) a where- zeroVector = zeroVector :* zeroVector-- a *^ (x :* y) = (a *^ x) :* (a *^ y)-- (x :* y) ^/ a = (x ^/ a) :* (y ^/ a)-- negateVector (x :* y) = (negateVector x) :* (negateVector y)-- (x1 :* y1) ^+^ (x2 :* y2) = (x1 ^+^ x2) :* (y1 ^+^ y2)-- (x1 :* y1) ^-^ (x2 :* y2) = (x1 ^-^ x2) :* (y1 ^-^ y2)-- (x1 :* y1) `dot` (x2 :* y2) = (x1 `dot` x2) + (y1 `dot` y2)--infixr 8 :!---- | Strict list type.-data List a = Nil | !a :! !(List a)--continuous ''List---singleton :: a -> List a-singleton x = x :! Nil--instance Traversable List where- traverse _ Nil = pure Nil- traverse f (x :! xs) = (:!) <$> (f x) <*> (traverse f xs)--instance IsList (List a) where- type Item (List a) = a-- fromList [] = Nil- fromList (x : xs) = x :! fromList xs-- toList Nil = []- toList (x :! xs) = x : toList xs----- | Remove the last element from a list if there is one, otherwise--- return 'Nil'.-init' :: List a -> List a-init' Nil = Nil-init' (_ :! Nil) = Nil-init' (x :! xs) = x :! init' xs---- | Reverse a list.-reverse' :: List a -> List a-reverse' l = rev l Nil- where- rev Nil a = a- rev (x:!xs) a = rev xs (x:!a)- --- | Returns @'Nothing''@ on an empty list or @'Just'' a@ where @a@ is the--- first element of the list.-listToMaybe' :: List a -> Maybe' a-listToMaybe' Nil = Nothing'-listToMaybe' (x :! _) = Just' x---- | Append two lists.-(+++) :: List a -> List a -> List a-(+++) Nil ys = ys-(+++) (x:!xs) ys = x :! xs +++ ys---map' :: (a -> b) -> List a -> List b-map' _ Nil = Nil-map' f (x :! xs) = f x :! map' f xs--zip' :: List a -> List b -> List (a :* b)-zip' Nil _ = Nil-zip' _ Nil = Nil-zip' (x :! xs) (y :! ys) = (x :* y) :! zip' xs ys--zipWith' :: (a -> b -> c) -> List a -> List b -> List c-zipWith' _ Nil _ = Nil-zipWith' _ _ Nil = Nil-zipWith' f (x :! xs) (y :! ys) = f x y :! zipWith' f xs ys----- | A version of 'map' which can throw out elements. In particular,--- the function argument returns something of type @'Maybe'' b@. If--- this is 'Nothing'', no element is added on to the result list. If--- it is @'Just'' b@, then @b@ is included in the result list.-mapMaybe' :: (a -> Maybe' b) -> List a -> List b-mapMaybe' _ Nil = Nil-mapMaybe' f (x:!xs) =- let rs = mapMaybe' f xs in- case f x of- Nothing' -> rs- Just' r -> r:!rs--union' :: (Eq a) => List a -> List a -> List a-union' = unionBy' (==)--unionBy' :: (a -> a -> Bool) -> List a -> List a -> List a-unionBy' eq xs ys = xs +++ foldl (flip (deleteBy' eq)) (nubBy' eq ys) xs--delete' :: (Eq a) => a -> List a -> List a-delete' = deleteBy' (==)--deleteBy' :: (a -> a -> Bool) -> a -> List a -> List a-deleteBy' _ _ Nil = Nil-deleteBy' eq x (y :! ys) = if x `eq` y then ys else y :! deleteBy' eq x ys---nub' :: (Eq a) => List a -> List a-nub' = nubBy' (==)--nubBy' :: (a -> a -> Bool) -> List a -> List a-nubBy' _ Nil = Nil-nubBy' eq (x:!xs) = x :! nubBy' eq (filter' (\ y -> not (eq x y)) xs)--filter' :: (a -> Bool) -> List a -> List a-filter' _ Nil = Nil-filter' pred (x :! xs)- | pred x = x :! filter' pred xs- | otherwise = filter' pred xs--instance Foldable List where- - foldMap f = run where- run Nil = mempty- run (x :! xs) = f x <> run xs- foldr f = run where- run b Nil = b- run b (a :! as) = (run $! (f a b)) as- foldl f = run where- run a Nil = a- run a (b :! bs) = (run $! (f a b)) bs- elem a = run where- run Nil = False- run (x :! xs)- | a == x = True- | otherwise = run xs- - -instance Functor List where- fmap = map'--instance Eq a => Eq (List a) where- Nil == Nil = True- Nil == _ = False- _ == Nil = False- (x :! xs) == (y :! ys) = if x == y then xs == ys else False--instance Show a => Show (List a) where- show Nil = "Nil"- show (x :! xs) = show x ++ " :! " ++ show xs---- | Strict variant of 'Maybe'.-data Maybe' a = Just' !a | Nothing'--continuous ''Maybe'--instance Eq a => Eq (Maybe' a) where- Nothing' == Nothing' = True- Just' x == Just' y = x == y- _ == _ = False--instance Show a => Show (Maybe' a) where- show Nothing' = "Nothing'"- show (Just' x) = "Just' " ++ show x---- | takes a default value, a function, and a 'Maybe'' value. If the--- 'Maybe'' value is 'Nothing'', the function returns the default--- value. Otherwise, it applies the function to the value inside the--- 'Just'' and returns the result.-maybe' :: b -> (a -> b) -> Maybe' a -> b-maybe' n _ Nothing' = n-maybe' _ f (Just' x) = f x--fromMaybe' :: a -> Maybe' a -> a-fromMaybe' _ (Just' x) = x-fromMaybe' d Nothing' = d-
+ src/WidgetRattus.hs view
@@ -0,0 +1,30 @@+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+++-- | The bare-bones Asynchronous Rattus language. To program with streams,+-- you can use "WidgetRattus.Stream".++module WidgetRattus (+ -- * Asynchronous Rattus language primitives+ module WidgetRattus.Primitives,+ -- * Channels API+ module WidgetRattus.Channels,+ -- * Strict data types+ module WidgetRattus.Strict,+ -- * Derive class instance declarations+ module WidgetRattus.Derive,+ -- * Annotation+ WidgetRattus(..),+ -- * other+ mapO+ )+ where++import WidgetRattus.Plugin+import WidgetRattus.Strict+import WidgetRattus.Primitives+import WidgetRattus.Derive+import WidgetRattus.Channels++mapO :: Box (a -> b) -> O a -> O b+mapO f later = delay (unbox f (adv later))
+ src/WidgetRattus/Channels.hs view
@@ -0,0 +1,79 @@+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}++{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module WidgetRattus.Channels (+ timer,+ Producer (..),+ chan,+ C (..),+ delayC,+ wait,+ Chan+) where+import WidgetRattus.InternalPrimitives++import WidgetRattus.Plugin.Annotation+import WidgetRattus.Strict+import System.IO.Unsafe+import Data.IORef+import Unsafe.Coerce++-- | A type @p@ satisfying @Producer p a@ is essentially a signal that+-- produces values of type @a@ but it might not produce such values at+-- each tick.+class Producer p a | p -> a where+ -- | Get the current value of the producer if any.+ getCurrent :: p -> Maybe' a+ -- | Get the next state of the producer. Morally, the type of this+ -- method should be+ --+ -- > getNext :: p -> (exists q. Producer q a => O q)+ --+ -- We encode the existential type using continuation-passing style.+ getNext :: p -> (forall q. Producer q a => O q -> b) -> b++instance Producer p a => Producer (O p) a where+ getCurrent _ = Nothing'+ getNext p cb = cb p++instance Producer p a => Producer (Box p) a where+ getCurrent p = getCurrent (unbox p)+ getNext p cb = getNext (unbox p) cb++newtype C a = C {unC :: IO a} deriving (Functor, Applicative, Monad)++chan :: C (Chan a)+chan = C (Chan <$> atomicModifyIORef nextFreshChannel (\ x -> (x - 1, x)))++delayC :: O (C a) -> C (O a)+delayC d = return (delay (unsafePerformIO (unC (adv d))))++{-# ANN wait AllowRecursion #-}+wait :: Chan a -> O a+wait (Chan ch) = Delay (singletonClock ch) (lookupInp ch) ++{-# NOINLINE nextFreshChannel #-}+nextFreshChannel :: IORef InputChannelIdentifier+nextFreshChannel = unsafePerformIO (newIORef (-1))++++{-# ANN lookupInp AllowRecursion #-}+lookupInp :: InputChannelIdentifier -> InputValue -> a+lookupInp _ (OneInput _ v) = unsafeCoerce v+lookupInp ch (MoreInputs ch' v more) = if ch' == ch then unsafeCoerce v else lookupInp ch more++-- | @timer n@ produces a delayed computation that ticks every @n@+-- milliseconds. In particular @mkSig (timer n)@ is a signal that+-- produces a new value every #n# milliseconds.+timer :: Int -> Box (O ())+timer d = Box (Delay (singletonClock (d `max` 10)) (\ _ -> ()))
+ src/WidgetRattus/Derive.hs view
@@ -0,0 +1,117 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ExistentialQuantification #-}+++module WidgetRattus.Derive (continuous) where++import WidgetRattus.InternalPrimitives+import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Control.Monad+++data DataInfo = forall flag . DataInfo Cxt Name [TyVarBndr flag] [Con] [DerivClause] ++{-|+ This function provides a list (of the given length) of new names based+ on the given string.+-}+newNames :: Int -> String -> Q [Name]+newNames n name = replicateM n (newName name)+++{-|+ This is the @Q@-lifted version of 'abstractNewtype.+-}+abstractNewtypeQ :: Q Info -> Q (Maybe DataInfo)+abstractNewtypeQ = liftM abstractNewtype+++{-| Apply a class name to type arguments to construct a type class+ constraint.+-}++mkClassP :: Name -> [Type] -> Type+mkClassP name = foldl AppT (ConT name)+++{-| This function provides the name and the arity of the given data+constructor, and if it is a GADT also its type.+-}+normalCon :: Con -> [(Name,[StrictType], Maybe Type)]+normalCon (NormalC constr args) = [(constr, args, Nothing)]+normalCon (RecC constr args) = [(constr, map (\(_,s,t) -> (s,t)) args, Nothing)]+normalCon (InfixC a constr b) = [(constr, [a,b], Nothing)]+normalCon (ForallC _ _ constr) = normalCon constr+normalCon (GadtC (constr:_) args typ) = [(constr,args,Just typ)]+normalCon (RecGadtC (constr : _) args typ) = [(constr,map dropFst args,Just typ)]+ where dropFst (_,x,y) = (x,y)+normalCon _ = error "missing case for 'normalCon'"++normalCon' :: Con -> [(Name,[Type], Maybe Type)]+normalCon' con = map conv (normalCon con)+ where conv (n, ts, t) = (n, map snd ts, t)+ +mkInstanceD :: Cxt -> Type -> [Dec] -> Dec+mkInstanceD cxt ty decs = InstanceD Nothing cxt ty decs++{-|+ This function returns the name of a bound type variable+-}+tyVarBndrName (PlainTV n _) = n+tyVarBndrName (KindedTV n _ _) = n++{-|+ This function abstracts away @newtype@ declaration, it turns them into+ @data@ declarations.+-}+abstractNewtype :: Info -> Maybe DataInfo+abstractNewtype (TyConI (NewtypeD cxt name args _ constr derive))+ = Just (DataInfo cxt name args [constr] derive)+abstractNewtype (TyConI (DataD cxt name args _ constrs derive))+ = Just (DataInfo cxt name args constrs derive)+abstractNewtype _ = Nothing++continuous :: Name -> Q [Dec]+continuous fname = do+ Just (DataInfo _cxt name args constrs _deriving) <- abstractNewtypeQ $ reify fname+ let argNames = map (VarT . tyVarBndrName) args+ complType = foldl AppT (ConT name) argNames+ preCond = map (mkClassP ''Continuous . (: [])) argNames+ classType = AppT (ConT ''Continuous) complType+ let constrs' = concatMap normalCon' constrs+ progressAndNextDecl <- funD 'progressAndNext (map genProgressAndNext constrs')+ progressInternalDecl <- funD 'progressInternal (map genProgressInternal constrs')+ nextProgressDecl <- funD 'nextProgress (map genNextProgress constrs')+ return [mkInstanceD preCond classType [progressAndNextDecl,progressInternalDecl,nextProgressDecl]]+ where genProgressAndNext (constr, args,_) = do+ let n = length args+ varNs <- newNames n "x"+ varNsR <- newNames n "y"+ varNsS <- newNames n "z"+ varIn <- newName "_inp"+ let pat = ConP constr [] $ map VarP varNs++ progressInternalExp <- [|progressAndNext|]+ let lets = zipWith3 (\ x y z -> ValD (TupP [VarP y, VarP z]) (NormalB (progressInternalExp `AppE` VarE varIn `AppE` VarE x)) []) varNs varNsR varNsS+ clockUnionExp <- [|clockUnion|]+ result <- appsE ( conE constr : (map varE varNsR))+ clock <- if n == 0 then [|emptyClock|] else return (foldl1 (\ x y -> (clockUnionExp `AppE` x) `AppE` y) (map VarE varNsS))+ let body = LetE lets (TupE [Just result, Just clock])+ return $ Clause [VarP varIn, pat] (NormalB body) []+ genProgressInternal (constr, args,_) = do+ let n = length args+ varNs <- newNames n "x"+ varIn <- newName "_inp"+ let pat = ConP constr [] $ map VarP varNs+ allVars = map varE varNs+ inpVar = varE varIn+ body <- appsE ( conE constr : (map (\ x -> [|progressInternal $inpVar $x|]) allVars))+ return $ Clause [VarP varIn, pat] (NormalB body) []+ genNextProgress (constr, args,_) = do+ let n = length args+ varNs <- newNames n "x"+ let pat = ConP constr [] $ map VarP varNs+ allVars = map varE varNs+ body <- if n == 0 then [|emptyClock|] else foldl1 (\ x y -> [|clockUnion $x $y|]) ((map (\ x -> [|nextProgress $x|]) allVars))+ return $ Clause [pat] (NormalB body) []
+ src/WidgetRattus/Future.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+++-- | Programming with futures.++module WidgetRattus.Future+ ( F(..)+ , SigF(..)+ , mkSigF+ , mkSigF'+ , current+ , future+ , bindF+ , mapF+ , sync+ , syncF+ , switchAwait+ , switch+ , switchS+ , filterMap+ , filterMapAwait+ , filterAwait+ , filter+ , trigger+ , triggerAwait+ , map+ , mapAwait+ , zipWith+ , zipWithAwait+ , fromSig+ , scan+ , scanAwait+ )++where++import WidgetRattus+import WidgetRattus.Signal (Sig(..))+import Prelude hiding (map, filter, zipWith)++newtype OneShot a = OneShot (F a)++instance Producer (OneShot a) a where+ getCurrent (OneShot (Now x)) = Just' x+ getCurrent (OneShot (Wait _)) = Nothing'++ getNext (OneShot (Now _)) cb = cb (never :: O (OneShot a))+ getNext (OneShot (Wait x)) cb = cb (delay (OneShot (adv x)))++instance Producer p a => Producer (F p) a where+ getCurrent (Now x) = getCurrent x+ getCurrent (Wait _) = Nothing'+ + getNext (Now x) cb = getNext x cb+ getNext (Wait x) cb = cb x++instance Producer (SigF a) a where+ getCurrent (x :>: _) = Just' x+ getNext (_ :>: xs) cb = cb xs++++-- | @F a@ will produces a value of type @a@ after zero or more ticks+-- of some clocks+data F a = Now !a | Wait !(O (F a))++++bindF :: F a -> Box (a -> F b) -> F b+bindF (Now x) f = unbox f x+bindF (Wait x) f = Wait (delay (bindF (adv x) f))++mapF :: Box (a -> b) -> F a -> F b+mapF f d = d `bindF` (box (\ x -> Now (unbox f x)))+++sync :: O (F a) -> O (F b) -> O (F a :* F b)+sync x y = delay (case select x y of+ Fst x' y' -> (x' :* Wait y')+ Snd x' y' -> (Wait x' :* y')+ Both x' y' -> (x' :* y'))++syncF :: (Stable a, Stable b) => F a -> F b -> F (a :* b)+syncF (Now x) (Now y) = Now (x :* y)+syncF (Wait x) (Now y) = Wait (delay (syncA (adv x) y))+syncF (Now x) (Wait y) = Wait (delay (syncB x (adv y)))+syncF (Wait x) (Wait y) = Wait (delay (case select x y of+ Fst x' y' -> syncF x' (Wait y')+ Snd x' y' -> syncF (Wait x') y'+ Both x' y' -> syncF x' y'+ )) ++syncA :: (Stable b) => F a -> b -> F (a :* b)+syncA (Now x) y = Now (x :* y)+syncA (Wait x) y = Wait (delay (syncA (adv x) y))+++syncB :: (Stable a) => a -> F b -> F (a :* b)+syncB x (Now y) = Now (x :* y)+syncB x (Wait y) = Wait (delay (syncB x (adv y)))+++-- | @SigF a@ is a signal of values of type @a@. In contrast to 'Sig',+-- 'SigF' supports the 'filter' and 'filterMap' functions.+data SigF a = !a :>: !(O (F (SigF a)))+++-- | Get the current value of a signal.+current :: SigF a -> a+current (x :>: _) = x+++-- | Get the future the signal.+future :: SigF a -> O (F (SigF a))+future (_ :>: xs) = xs+++mkSigF :: Box (O a) -> F (SigF a)+mkSigF b = Wait (mkSigF' b) where++mkSigF' :: Box (O a) -> O (F (SigF a))+mkSigF' b = delay (Now (adv (unbox b) :>: mkSigF' b))+++fromSig :: Sig a -> SigF a+fromSig (x ::: xs) = x :>: delay (Now (fromSig (adv xs)))++ +switchAwait :: F (SigF a) -> F (SigF a) -> F(SigF a)+switchAwait _ (Now ys) = Now ys+switchAwait (Now (x :>: xs)) (Wait ys) = Now (x :>: delay (uncurry' switchAwait (adv (sync xs ys)) ))+switchAwait (Wait xs) (Wait ys) = Wait (delay (uncurry' switchAwait (adv (sync xs ys)) ))++switch :: SigF a -> F (SigF a) -> SigF a+switch _ (Now ys) = ys+switch (x :>: xs) (Wait ys) = x :>: delay (uncurry' switchAwait (adv (sync xs ys)))++switchS :: Stable a => SigF a -> F (a -> SigF a) -> SigF a+switchS (x :>: _) (Now f) = f x+switchS (x :>: xs) (Wait ys) = x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys)))++switchAwaitS :: Stable a => a -> F (SigF a) -> F (a -> SigF a) -> F (SigF a)+switchAwaitS _ (Now (x :>: _)) (Now f) = Now (f x)+switchAwaitS _ (Now (x :>: xs)) (Wait ys) =+ Now (x :>: delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))+switchAwaitS x (Wait _) (Now f) = Now (f x)+switchAwaitS x (Wait xs) (Wait ys) = Wait (delay (uncurry' (switchAwaitS x) (adv (sync xs ys))))++++filterMapAwait :: Box (a -> Maybe' b) -> F(SigF a) -> F (SigF b)+filterMapAwait f (Wait xs) = Wait (delay (filterMapAwait f (adv xs)))+filterMapAwait f (Now (x :>: xs)) = case unbox f x of+ Just' y -> Now (y :>: delay (filterMapAwait f (adv xs)))+ Nothing' -> Wait (delay (filterMapAwait f (adv xs)))++filterMap :: Box (a -> Maybe' b) -> SigF a -> F (SigF b)+filterMap f xs = filterMapAwait f (Now xs)+++filterAwait :: Box (a -> Bool) -> F( SigF a) -> F (SigF a)+filterAwait p = filterMapAwait (box (\ x -> if unbox p x then Just' x else Nothing'))++filter :: Box (a -> Bool) -> SigF a -> F (SigF a)+filter p = filterMap (box (\ x -> if unbox p x then Just' x else Nothing'))++trigger :: Stable b => Box (a -> b -> c) -> SigF a -> SigF b -> SigF c+trigger f (a :>: as) (b :>: bs) =+ unbox f a b :>:+ delay (uncurry' (trigger' b f) (adv (sync as bs)))++triggerAwait :: Stable b => Box (a -> b -> c) -> F (SigF a) -> SigF b -> F (SigF c)+triggerAwait f (Now (a :>: as)) (b :>: bs)+ = Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+triggerAwait f (Wait as) (b :>: bs)+ = Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))++trigger' :: Stable b => b -> Box (a -> b -> c) -> F (SigF a) -> F (SigF b) -> F (SigF c)+trigger' b f (Now (a :>: as)) (Wait bs) =+ Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' _ f (Now (a :>: as)) (Now (b :>: bs)) =+ Now (unbox f a b :>: delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' b f (Wait as) (Wait bs) =+ Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))+trigger' _ f (Wait as) (Now (b :>: bs)) =+ Wait (delay (uncurry' (trigger' b f) (adv (sync as bs))))+++mapAwait :: Box (a -> b) -> F (SigF a) -> F (SigF b)+mapAwait f (Now (x :>: xs)) = Now (unbox f x :>: delay (mapAwait f (adv xs)))+mapAwait f (Wait xs) = Wait (delay (mapAwait f (adv xs)))++map :: Box (a -> b) -> SigF a -> SigF b+map f (x :>: xs) = unbox f x :>: delay (mapAwait f (adv xs))++++zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> SigF a -> SigF b -> SigF c+zipWith f (a :>: as) (b :>: bs) = unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs)))++zipWithAwait :: (Stable a, Stable b) => Box(a -> b -> c) -> a -> b -> F (SigF a) -> F (SigF b) -> F (SigF c)+zipWithAwait f _ _ (Now (a :>: as)) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f _ b (Now (a :>: as)) (Wait bs) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f a _ (Wait as) (Now (b :>: bs)) = Now (unbox f a b :>: delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))+zipWithAwait f a b (Wait as) (Wait bs) = Wait (delay (uncurry' (zipWithAwait f a b) (adv (sync as bs))))++scan :: (Stable b) => Box(b -> a -> b) -> b -> SigF a -> SigF b+scan f acc (a :>: as) = acc' :>: delay (scanAwait f acc' (adv as))+ where acc' = unbox f acc a++scanAwait :: (Stable b) => Box (b -> a -> b) -> b -> F (SigF a) -> F (SigF b)+scanAwait f acc (Now (a :>: as)) = Now (acc' :>: delay (scanAwait f acc' (adv as)))+ where acc' = unbox f acc a+scanAwait f acc (Wait as) = Wait (delay (scanAwait f acc (adv as)))
+ src/WidgetRattus/InternalPrimitives.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}++module WidgetRattus.InternalPrimitives where++import Prelude hiding (Left, Right)+import Data.IntSet (IntSet)+import qualified Data.IntSet as IntSet+import Data.IORef+import Control.Concurrent.MVar+import System.IO.Unsafe+import System.Mem.Weak+import Control.Monad++-- An input channel is identified by an integer. The programmer should not know about it.+type InputChannelIdentifier = Int++type Clock = IntSet++singletonClock :: InputChannelIdentifier -> Clock+singletonClock = IntSet.singleton++emptyClock :: Clock+emptyClock = IntSet.empty++clockUnion :: Clock -> Clock -> Clock+clockUnion = IntSet.union++channelMember :: InputChannelIdentifier -> Clock -> Bool+channelMember = IntSet.member++data InputValue where+ OneInput :: !InputChannelIdentifier -> !a -> InputValue+ MoreInputs :: !InputChannelIdentifier -> !a -> !InputValue -> InputValue++inputInClock :: InputValue -> Clock -> Bool+inputInClock (OneInput ch _) cl = channelMember ch cl+inputInClock (MoreInputs ch _ more) cl = channelMember ch cl || inputInClock more cl+++-- | The "later" type modality. A value @v@ of type @O 𝜏@ consists of+-- two components: Its clock, denoted @cl(v)@, and a delayed+-- computation that will produce a value of type @𝜏@ as soon as the+-- clock @cl(v)@ ticks. The clock @cl(v)@ is only used for type+-- checking and is not directly accessible, whereas the delayed+-- computation is accessible via 'adv' and 'select'.++data O a = Delay !Clock (InputValue -> a)++-- | The return type of the 'select' primitive.+data Select a b = Fst !a !(O b) | Snd !(O a) !b | Both !a !b++rattusError pr = error (pr ++ ": Did you forget to mark this as Async Rattus code?")++-- | This is the constructor for the "later" modality 'O':+--+-- > Γ ✓θ ⊢ t :: 𝜏+-- > --------------------+-- > Γ ⊢ delay t :: O 𝜏+--+-- The typing rule requires that its argument @t@ typecheck with an+-- additional tick @✓θ@ of some clock @θ@.+{-# INLINE [1] delay #-}+delay :: a -> O a+delay _ = rattusError "delay"++extractClock :: O a -> Clock+extractClock (Delay cl _) = cl++{-# INLINE [1] adv' #-}+adv' :: O a -> InputValue -> a+adv' (Delay _ f) inp = f inp+++-- | This is the eliminator for the "later" modality 'O':+--+-- > Γ ⊢ t :: O 𝜏 Γ' tick-free+-- > ---------------------------------+-- > Γ ✓cl(t) Γ' ⊢ adv t :: 𝜏+--+-- It requires that a tick @✓θ@ is in the context whose clock matches+-- exactly the clock of @t@, i.e. @θ = cl(t)@.++{-# INLINE [1] adv #-}+adv :: O a -> a+adv _ = rattusError "adv"++-- | If we want to eliminate more than one delayed computation, i.e.\+-- two @s :: O σ@ and @t :: O 𝜏@, we need to use 'select' instead of+-- just 'adv'.+--+-- > Γ ⊢ s :: O σ Γ ⊢ t :: O 𝜏 Γ' tick-free+-- > --------------------------------------------------+-- > Γ ✓cl(s)⊔cl(t) Γ' ⊢ select s t :: Select σ 𝜏+--+-- It requires that we have a tick @✓θ@ in the context whose clock+-- matches the union of the clocks of @s@ and @t@, i.e. @θ =+-- cl(s)⊔cl(t)@. The union of two clocks ticks whenever either of the+-- two clocks ticks, i.e. @cl(s)⊔cl(t)@, whenever @cl(s)@ or @cl(t)@+-- ticks.+--+-- That means there are three possible outcomes, which are reflected+-- in the result type of @select s t@. A value of @Select σ 𝜏@ is+-- either+--+-- * a value of type @σ@ and a delayed computation of type @O 𝜏@, if+-- @cl(s)@ ticks before @cl(t)@,+--+-- * a value of type @𝜏@ and a delayed computation of type @O σ@, if+-- @cl(t)@ ticks before @cl(s)@, or+--+-- * a value of type @σ@ and a value of type @𝜏@, if @cl(s)@ and+-- * @cl(s)@ tick simultaneously.+++{-# INLINE [1] select #-}+select :: O a -> O b -> Select a b+select _ _ = rattusError "select"++select' :: O a -> O b -> InputValue -> Select a b+select' a@(Delay clA inpFA) b@(Delay clB inpFB) inp+ = if inputInClock inp clA then+ if inputInClock inp clB then Both (inpFA inp) (inpFB inp)+ else Fst (inpFA inp) b+ else Snd a (inpFB inp)++++-- | The clock of @never :: O 𝜏@ will never tick, i.e. it will never+-- produce a value of type @𝜏@. With 'never' we can for example+-- implement the constant signal @x ::: never@ of type @Sig a@ for any @x ::+-- a@.+never :: O a+never = Delay emptyClock (error "Trying to adv on the 'never' delayed computation")++-- | A type is @Stable@ if it is a strict type and the later modality+-- @O@ and function types only occur under @Box@.+--+-- For example, these types are stable: @Int@, @Box (a -> b)@, @Box (O+-- Int)@, @Box (Sig a -> Sig b)@.+--+-- But these types are not stable: @[Int]@ (because the list type is+-- not strict), @Int -> Int@, (function type is not stable), @O+-- Int@, @Sig Int@.++class Stable a where++++-- | The "stable" type modality. A value of type @Box a@ is a+-- time-independent computation that produces a value of type @a@.+-- Use 'box' and 'unbox' to construct and consume 'Box'-types.+data Box a = Box a+++-- | This is the constructor for the "stable" modality 'Box':+--+-- > Γ☐ ⊢ t :: 𝜏+-- > --------------------+-- > Γ ⊢ box t :: Box 𝜏+--+-- where Γ☐ is obtained from Γ by removing all ticks and all variables+-- @x :: 𝜏@, where 𝜏 is not a stable type.++{-# INLINE [1] box #-}+box :: a -> Box a+box x = Box x+++-- | This is the eliminator for the "stable" modality 'Box':+--+-- > Γ ⊢ t :: Box 𝜏+-- > ------------------+-- > Γ ⊢ unbox t :: 𝜏+{-# INLINE [1] unbox #-}+unbox :: Box a -> a+unbox (Box d) = d+++defaultPromote :: Continuous a => a -> Box a+defaultPromote x = unsafePerformIO $ + do r <- newIORef x+ r' <- mkWeakIORef r (return ()) + modifyIORef promoteStore (ContinuousData r' :)+ return (Box (unsafePerformIO $ readIORef r))+++class Continuous p where+ -- | Computes the same as 'progressInternal' and 'nextProgress'. In+ -- particular @progressAndNext inp v = (progressInternal inp v,+ -- nextProgress (progressInternal inp v))@.+ progressAndNext :: InputValue -> p -> (p , Clock)++ -- | Progresses the continuous value, given the input value from+ -- some channel+ progressInternal :: InputValue -> p -> p+ -- | Computes the set of channels that the continuous value is+ -- depending on. That is if @nextProgress v = cl@ and a new input+ -- @inp@ on channel @ch@ arrives, then @progressInternal inp v = v@+ -- if @not (ch `channelMember` cl)@.+ nextProgress :: p -> Clock + promoteInternal :: p -> Box p+ promoteInternal = defaultPromote++-- For stable types we can circumvent the "promote store".+instance {-# OVERLAPPABLE #-} Stable a => Continuous a where+ progressAndNext _ x = (x , emptyClock) + progressInternal _ x = x+ nextProgress _ = emptyClock+ promoteInternal = Box++data ContinuousData where+ ContinuousData :: Continuous a => !(Weak (IORef a)) -> ContinuousData++-- TODO: The list type needs to be replaced by a more efficient+-- mutable data structure.+{-# NOINLINE promoteStore #-}+promoteStore :: IORef [ContinuousData]+promoteStore = unsafePerformIO (newIORef [])++{-# NOINLINE progressPromoteStoreMutex #-}+progressPromoteStoreMutex :: MVar ()+progressPromoteStoreMutex = unsafePerformIO (newMVar ())+++-- | Atomic version of 'progressPromoteStore'.++progressPromoteStoreAtomic :: InputValue -> IO ()+progressPromoteStoreAtomic inp = do+ takeMVar progressPromoteStoreMutex+ progressPromoteStore inp+ putMVar progressPromoteStoreMutex ()+++-- | For promote to work, its argument must be stored in the "promote+-- store", and whenenver an input is received on some channel, all+-- values in the "promote store" must be advanced (using+-- 'progressInternal').++progressPromoteStore :: InputValue -> IO ()+progressPromoteStore inp = do + xs <- atomicModifyIORef promoteStore (\x -> ([],x))+ xs' <- filterM run xs+ atomicModifyIORef promoteStore (\x -> (x ++ xs',()))+ where run (ContinuousData x) = do+ d <- deRefWeak x+ case d of+ Nothing -> return False+ Just x -> modifyIORef' x (progressInternal inp) >> return True++promote :: Continuous a => a -> Box a+promote x = promoteInternal x++newtype Chan a = Chan InputChannelIdentifier++{-# RULES+ "unbox/box" forall x. unbox (box x) = x+ #-}+++{-# RULES+ "box/unbox" forall x. box (unbox x) = x+ #-}
+ src/WidgetRattus/Plugin.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE CPP #-}+++-- | The plugin to make it all work.++module WidgetRattus.Plugin (plugin, WidgetRattus(..)) where+import WidgetRattus.Plugin.StableSolver+import WidgetRattus.Plugin.ScopeCheck+import WidgetRattus.Plugin.Strictify+import WidgetRattus.Plugin.SingleTick+import WidgetRattus.Plugin.CheckClockCompatibility+import WidgetRattus.Plugin.Utils+import WidgetRattus.Plugin.Annotation+import WidgetRattus.Plugin.Transform++import Prelude hiding ((<>))++import Control.Monad+import Data.Maybe+import Data.List+import Data.Data hiding (tyConName)+import qualified Data.Set as Set+import Data.Set (Set)++import qualified GHC.LanguageExtensions as LangExt++import GHC.Plugins+import GHC.Tc.Types++-- | Use this to enable Asynchronous Rattus' plugin, either by supplying the option+-- @-fplugin=WidgetRattus.Plugin@ directly to GHC, or by including the+-- following pragma in each source file:+-- +-- > {-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+plugin :: Plugin+plugin = defaultPlugin {+ installCoreToDos = install,+ pluginRecompile = purePlugin,+ typeCheckResultAction = typechecked,+ tcPlugin = tcStable,+ driverPlugin = updateEnv+ }+++data Options = Options {debugMode :: Bool}+++-- | Enable the @Strict@ language extension.+updateEnv :: [CommandLineOption] -> HscEnv -> IO HscEnv+updateEnv _ env = return env {hsc_dflags = update (hsc_dflags env) } + where update fls = xopt_set fls LangExt.Strict++typechecked :: [CommandLineOption] -> ModSummary -> TcGblEnv -> TcM TcGblEnv+typechecked _ _ env = checkAll env >> return env++install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]+install opts todo = case find findSamePass todo of -- check that we don't run the transformation twice+ Nothing -> return (strPass : todo) -- (e.g. if the "-fplugin" option is used twice)+ _ -> return todo+ where name = "Async Rattus strictify"+ strPass = CoreDoPluginPass name (strictifyProgram Options{debugMode = dmode})+ dmode = "debug" `elem` opts+ findSamePass (CoreDoPluginPass s _) = s == name+ findSamePass _ = False+ ++-- | Apply the following operations to all Asynchronous Rattus definitions in the+-- program:+--+-- * Transform into single tick form (see SingleTick module)+-- * Check whether lazy data types are used (see Strictify module)+-- * Transform into call-by-value form (see Strictify module)++strictifyProgram :: Options -> ModGuts -> CoreM ModGuts+strictifyProgram opts guts = do+ newBinds <- mapM (strictify opts guts) (mg_binds guts)+ return guts { mg_binds = newBinds }++strictify :: Options -> ModGuts -> CoreBind -> CoreM CoreBind+strictify opts guts b@(Rec bs) = do+ let debug = debugMode opts+ tr <- liftM or (mapM (shouldProcessCore guts . fst) bs)+ if tr then do+ let vs = map fst bs+ es' <- mapM (\ (v,e) -> do+ processCore <- shouldProcessCore guts v+ if not processCore+ then do+ when debug $ putMsg $ text "Skipping binding: " <> ppr v+ return e+ else checkAndTransform guts (Set.fromList vs) debug v e+ ) bs+ when debug $ putMsg $ "Plugin | result of transformation: " <> ppr es'+ return (Rec (zip vs es'))+ else return b+strictify opts guts b@(NonRec v e) = do+ let debug = debugMode opts+ when debug $ putMsg $ text "Processing binding: " <> ppr v <> text " | Non-recursive binding"+ when debug $ putMsg $ text "Expr: " <> ppr e+ processCore <- shouldProcessCore guts v+ if not processCore then do+ when debug $ putMsg $ text "Skipping binding: " <> ppr v+ return b+ else do+ transformed <- checkAndTransform guts Set.empty debug v e+ when debug $ putMsg $ "Plugin | result of transformation: " <> ppr transformed+ return $ NonRec v transformed++checkAndTransform :: ModGuts -> Set Var -> Bool -> Var -> CoreExpr -> CoreM CoreExpr+checkAndTransform guts recursiveSet debug v e = do+ when debug $ putMsg $ text "Processing binding: " <> ppr v+ when debug $ putMsg $ text "Expr: " <> ppr e+ allowRec <- allowRecursion guts v+ singleTick <- toSingleTick e+ when debug $ putMsg $ text "Single-tick: " <> ppr singleTick+ lazy <- allowLazyData guts v+ when (not lazy) $ checkStrictData (SCxt (nameSrcSpan $ getName v)) singleTick+ when debug $ putMsg $ text "Strict single-tick: " <> ppr singleTick+ checkExpr CheckExpr{ recursiveSet = recursiveSet, oldExpr = e,+ verbose = debug,+ allowRecExp = allowRec} singleTick+ transform singleTick++getModuleAnnotations :: Data a => ModGuts -> [a]+getModuleAnnotations guts = anns'+ where anns = filter (\a-> case ann_target a of+ ModuleTarget m -> m == (mg_module guts)+ _ -> False) (mg_anns guts)+ anns' = mapMaybe (fromSerialized deserializeWithData . ann_value) anns+++++allowLazyData :: ModGuts -> CoreBndr -> CoreM Bool+allowLazyData guts bndr = do+ l <- annotationsOn guts bndr :: CoreM [WidgetRattus]+ return (AllowLazyData `elem` l)++allowRecursion :: ModGuts -> CoreBndr -> CoreM Bool+allowRecursion guts bndr = do+ l <- annotationsOn guts bndr :: CoreM [WidgetRattus]+ return (AllowRecursion `elem` l)++expectError :: ModGuts -> CoreBndr -> CoreM Bool+expectError guts bndr = do+ l <- annotationsOn guts bndr :: CoreM [InternalAnn]+ return $ ExpectError `elem` l+++shouldProcessCore :: ModGuts -> CoreBndr -> CoreM Bool+shouldProcessCore guts bndr = do+ expectScopeError <- expectError guts bndr+ return (userFunction bndr && not expectScopeError)++annotationsOn :: (Data a) => ModGuts -> CoreBndr -> CoreM [a]+annotationsOn guts bndr = do+ (_,anns) <- getAnnotations deserializeWithData guts+ return $+ lookupWithDefaultUFM anns [] (varName bndr) +++ getModuleAnnotations guts
+ src/WidgetRattus/Plugin/Annotation.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE DeriveDataTypeable #-}+module WidgetRattus.Plugin.Annotation (WidgetRattus(..), InternalAnn (..)) where++import Data.Data++-- | By default all Async Rattus functions are checked for use of lazy+-- data types, since these may cause memory leaks. If any lazy data+-- types are used, a warning is issued. These warnings can be disabled+-- by annotating the module or the function with 'AllowLazyData'+--+-- > {-# ANN myFunction AllowLazyData #-}+-- >+-- > {-# ANN module AllowLazyData #-}+--+-- Async Rattus only allows guarded recursion, i.e. recursive calls+-- must occur in the scope of a tick. Structural recursion over strict+-- data types is safe as well, but is currently not checked. To+-- disable the guarded recursion check, annotate the module or+-- function with 'AllowRecursion'.+-- +-- > {-# ANN myFunction AllowRecursion #-}+-- >+-- > {-# ANN module AllowRecursion #-}+++data WidgetRattus = AllowLazyData | AllowRecursion deriving (Typeable, Data, Show, Ord, Eq)+++-- | This annotation type is for internal use only.+data InternalAnn = ExpectError | ExpectWarning deriving (Typeable, Data, Show, Eq, Ord)
+ src/WidgetRattus/Plugin/CheckClockCompatibility.hs view
@@ -0,0 +1,261 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE CPP #-}++-- | This module implements the check that the transformed code is+-- typable in the single tick calculus.++module WidgetRattus.Plugin.CheckClockCompatibility+ (checkExpr, CheckExpr (..)) where++import GHC.Types.Tickish+import GHC.Plugins++import WidgetRattus.Plugin.Utils+import qualified WidgetRattus.Plugin.PrimExpr as Prim+import Prelude hiding ((<>))+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Map (Map)+import qualified Data.Map as Map+import Data.Maybe (isJust)+import Control.Monad (foldM, when)+import Control.Applicative ((<|>))+import System.Exit (exitFailure)++type LCtx = Set Var+data HiddenReason = BoxApp | AdvApp | NestedRec Var | FunDef | DelayApp+type Hidden = Map Var HiddenReason++data TypeError = TypeError SrcSpan SDoc+++data Ctx = Ctx+ { current :: LCtx,+ hidden :: Hidden,+ earlier :: Maybe LCtx,+ srcLoc :: SrcSpan,+ recDef :: Set Var, -- ^ recursively defined variables + stableTypes :: Set Var,+ allowRecursion :: Bool,+ allowGuardedRec :: Bool+ }++hasTick :: Ctx -> Bool+hasTick = isJust . earlier++stabilize :: HiddenReason -> Ctx -> Ctx+stabilize hr c = c+ {current = Set.empty,+ earlier = Nothing,+ hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid,+ allowGuardedRec = False+ }+ where ctxHid = maybe (current c) (Set.union (current c)) (earlier c)++data Scope = Hidden SDoc | Visible++getScope :: Ctx -> Var -> Scope+getScope c v =+ if v `Set.member` recDef c then+ if allowGuardedRec c || allowRecursion c || typeClassFunction v then Visible+ else Hidden ("(Mutually) recursive call to " <> ppr v <> " must occur under delay")+ else case Map.lookup v (hidden c) of+ Just reason ->+ if (isStable (stableTypes c) (varType v)) then Visible+ else case reason of+ NestedRec rv ->+ if allowRecursion c then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope:"+ $$ "It appears in a local recursive definition (namely of " <> ppr rv <> ")"+ $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ BoxApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+ "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")++ FunDef -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")+ DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under two occurrences of delay and is a of a non-stable type " <> ppr (varType v))+ Nothing+ | maybe False (Set.member v) (earlier c) ->+ if isStable (stableTypes c) (varType v) then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+ "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ | Set.member v (current c) -> Visible+ | otherwise -> Visible++++pickFirst :: SrcSpan -> SrcSpan -> SrcSpan+pickFirst s@RealSrcSpan{} _ = s+pickFirst _ s = s++typeError :: Ctx -> Var -> SDoc -> TypeError+typeError ctx var = TypeError (pickFirst (srcLoc ctx) (nameSrcSpan (varName var)))++instance Outputable TypeError where+ ppr (TypeError srcLoc sdoc) = text "TypeError at " <> ppr srcLoc <> text ": " <> ppr sdoc++emptyCtx :: CheckExpr -> Ctx+emptyCtx c =+ Ctx { current = Set.empty,+ earlier = Nothing,+ hidden = Map.empty,+ srcLoc = noLocationInfo,+ recDef = recursiveSet c,+ stableTypes = Set.empty,+ allowRecursion = allowRecExp c,+ allowGuardedRec = False+ }++stabilizeLater :: Ctx -> Ctx+stabilizeLater c =+ case earlier c of+ Just earl -> c {earlier = Nothing,+ hidden = hidden c `Map.union` Map.fromSet (const FunDef) earl}+ Nothing -> c++isStableConstr :: Type -> CoreM (Maybe Var)+isStableConstr t =+ case splitTyConApp_maybe t of+ Just (con,[args]) ->+ case getNameModule con of+ Just (name, mod) ->+ if isRattModule mod && name == "Stable"+ then return (getTyVar_maybe args)+ else return Nothing+ _ -> return Nothing+ _ -> return Nothing++-- should be equatable+type SymbolicClock = Set Var++mkClock1 :: Var -> SymbolicClock+mkClock1 = Set.singleton++mkClock2 :: Var -> Var -> SymbolicClock+mkClock2 v1 v2 = Set.fromList [v1, v2]++newtype CheckResult = CheckResult{+ -- if present, contains the variable of the primitive applied so we can report its position+ -- in case of an error, and the clock for the primitive+ prim :: Maybe (Var, SymbolicClock)+}++instance Outputable CheckResult where+ ppr (CheckResult prim) = text "CheckResult {prim = " <> ppr prim <> text "}"++emptyCheckResult :: CheckResult+emptyCheckResult = CheckResult {prim = Nothing}++data CheckExpr = CheckExpr{+ recursiveSet :: Set Var,+ oldExpr :: Expr Var,+ verbose :: Bool,+ allowRecExp :: Bool+ }++checkExpr :: CheckExpr -> Expr Var -> CoreM ()+checkExpr c e = do+ when (verbose c) $ putMsg $ text "checkExpr: " <> ppr e+ res <- checkExpr' (emptyCtx c) e+ case res of+ Right _ -> do when (verbose c) $ putMsgS "checkExpr succeeded."+ Left (TypeError src doc) ->+ let printErrMsg = if verbose c+ then do+ printMessage SevError src ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$ doc)+ putMsgS "-------- old --------"+ putMsg $ ppr (oldExpr c)+ putMsgS "-------- new --------"+ putMsg (ppr e)+ + else do+ printMessage SevError noSrcSpan ("Internal error in Async Rattus Plugin: single tick transformation did not preserve typing." $$+ "Compile with flags \"-fplugin-opt WidgetRattus.Plugin:debug\" and \"-g2\" for detailed information")+ in do+ printErrMsg+ liftIO exitFailure+++checkExpr' :: Ctx -> Expr Var -> CoreM (Either TypeError CheckResult)+checkExpr' c (App e e') | isType e' || (not $ tcIsLiftedTypeKind $ typeKind $ exprType e')+ = checkExpr' c e+checkExpr' c@Ctx{current = cur, earlier = earl} expr@(App e e') =+ case Prim.isPrimExpr expr of+ Just (Prim.BoxApp _) ->+ checkExpr' (stabilize BoxApp c) e'+ Just (Prim.DelayApp f _) -> do+ let c' = case earl of+ Nothing -> c{current = Set.empty, earlier = Just cur, allowGuardedRec = True}+ Just earl' -> c{ current = Set.empty, earlier = Just cur, allowGuardedRec = True,+ hidden = hidden c `Map.union` Map.fromSet (const DelayApp) earl'}+ eRes <- checkExpr' c' e'+ case eRes of+ Left err -> return $ Left err+ Right (CheckResult {prim = Nothing}) -> return $ Left $ typeError c f (text "Each delay must contain an adv or select")+ Right _ -> return $ Right emptyCheckResult+ Just (Prim.AdvApp f _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use adv under delay")+ Just (Prim.AdvApp f (arg, _)) -> return $ Right $ CheckResult {prim = Just (f, mkClock1 arg)}+ Just (Prim.SelectApp f _ _) | not (hasTick c) -> return $ Left $ typeError c f (text "can only use select under delay")+ Just (Prim.SelectApp f (arg1, _) (arg2, _))-> return $ Right $ CheckResult {prim = Just (f, mkClock2 arg1 arg2)}+ Nothing -> checkBoth c e e'+checkExpr' c (Case e v _ alts) = do+ res <- checkExpr' c' e+ resAll <- mapM (\(Alt _ _ altE) -> checkExpr' c altE) alts+ foldM (fmap return . combine c) res resAll+ where c' = addVars [v] c+checkExpr' c (Lam v e)+ | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = do+ is <- isStableConstr (varType v)+ let c' = case is of+ Nothing -> c+ Just t -> c{stableTypes = Set.insert t (stableTypes c)}+ checkExpr' c' e+ | otherwise = checkExpr' (addVars [v] (stabilizeLater c)) e+checkExpr' _ (Type _) = return $ Right emptyCheckResult+checkExpr' _ (Lit _) = return $ Right emptyCheckResult+checkExpr' _ (Coercion _) = return $ Right emptyCheckResult+checkExpr' c (Tick (SourceNote span _name) e) =+ checkExpr' c{srcLoc = fromRealSrcSpan span} e+checkExpr' c (Tick _ e) = checkExpr' c e+checkExpr' c (Cast e _) = checkExpr' c e+checkExpr' c (Let (NonRec _ e1) e2) = do+ res1 <- checkExpr' c e1+ res2 <- checkExpr' c e2+ return $ combine c res1 res2+checkExpr' c (Let (Rec binds) e2) = do+ resAll <- mapM (\ (v,e) -> checkExpr' (c' v) e) binds+ res <- checkExpr' (addVars vs c) e2+ foldM (fmap return . combine c) res resAll+ where vs = map fst binds+ ctxHid = maybe (current c) (Set.union (current c)) (earlier c)+ c' v = c {current = Set.empty,+ earlier = Nothing,+ hidden = hidden c `Map.union`+ Map.fromSet (const (NestedRec v)) ctxHid,+ recDef = recDef c `Set.union` Set.fromList vs }+checkExpr' c (Var v)+ | tcIsLiftedTypeKind $ typeKind $ varType v = case getScope c v of+ Hidden reason -> return $ Left $ typeError c v reason+ Visible -> return $ Right emptyCheckResult+ | otherwise = return $ Right emptyCheckResult++addVars :: [Var] -> Ctx -> Ctx+addVars v c = c{current = Set.fromList v `Set.union` current c }++checkBoth :: Ctx -> CoreExpr -> CoreExpr -> CoreM (Either TypeError CheckResult)+checkBoth c e e' = do+ c1 <- checkExpr' c e+ c2 <- checkExpr' c e'+ return $ combine c c1 c2++-- Combines two CheckResults such that the clocks therein are compatible.+-- If both CheckResults have PrimVars, one is picked arbitrarily.+combine :: Ctx -> Either TypeError CheckResult -> Either TypeError CheckResult -> Either TypeError CheckResult+combine c eRes1 eRes2 = do+ res1 <- eRes1+ res2 <- eRes2+ case (res1, res2) of+ (CheckResult (Just (_, cl1)), CheckResult (Just (_, cl2))) | cl1 == cl2 -> Right res2+ (CheckResult (Just _), CheckResult (Just (p, _))) -> Left $ typeError c p "Only one adv/select allowed in a delay"+ (CheckResult maybeP, CheckResult maybeP') -> Right $ CheckResult {prim = maybeP <|> maybeP'}
+ src/WidgetRattus/Plugin/Dependency.hs view
@@ -0,0 +1,385 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE GADTs #-}++-- | This module is used to perform a dependency analysis of top-level+-- function definitions, i.e. to find out which defintions are+-- (mutual) recursive. To this end, this module also provides+-- functions to compute, bound variables and variable occurrences.++module WidgetRattus.Plugin.Dependency (dependency, HasBV (..),printBinds) where+++import GHC.Plugins+import GHC.Data.Bag+import GHC.Hs.Type+++import GHC.Hs.Extension+import GHC.Hs.Expr+import GHC.Hs.Pat+import GHC.Hs.Binds++#if __GLASGOW_HASKELL__ >= 904+import GHC.Parser.Annotation+#else+import Language.Haskell.Syntax.Extension+import GHC.Parser.Annotation+#endif+++import Data.Set (Set)+import qualified Data.Set as Set+import Data.Graph+import Data.Maybe+import Data.Either+import Prelude hiding ((<>))++++-- | Compute the dependencies of a bag of bindings, returning a list+-- of the strongly-connected components.+dependency :: Bag (LHsBindLR GhcTc GhcTc) -> [SCC (LHsBindLR GhcTc GhcTc, Set Var)]+dependency binds = map AcyclicSCC noDeps ++ catMaybes (map filterJust (stronglyConnComp (concat deps)))+ where (deps,noDeps) = partitionEithers $ map mkDep $ bagToList binds+ mkDep :: GenLocated l (HsBindLR GhcTc GhcTc) ->+ Either [(Maybe (GenLocated l (HsBindLR GhcTc GhcTc), Set Var), Name, [Name])]+ (GenLocated l (HsBindLR GhcTc GhcTc), Set Var)+ mkDep b =+ let dep = map varName $ Set.toList (getFV b)+ vars = getBV b in+ case Set.toList vars of+ (v:vs) -> Left ((Just (b,vars), varName v , dep) : map (\ v' -> (Nothing, varName v' , dep)) vs)+ [] -> Right (b,vars)+ filterJust (AcyclicSCC Nothing) = Nothing -- this should not happen+ filterJust (AcyclicSCC (Just b)) = Just (AcyclicSCC b)+ filterJust (CyclicSCC bs) = Just (CyclicSCC (catMaybes bs))+++printBinds (AcyclicSCC bind) = liftIO (putStr "acyclic bind: ") >> printBind (fst bind) >> liftIO (putStrLn "") +printBinds (CyclicSCC binds) = liftIO (putStr "cyclic binds: ") >> mapM_ (printBind . fst) binds >> liftIO (putStrLn "") +++printBind (L _ FunBind{fun_id = L _ name}) = + liftIO $ putStr $ (getOccString name ++ " ")+printBind (L _ (VarBind {var_id = name})) = liftIO $ putStr $ (getOccString name ++ " ")+#if __GLASGOW_HASKELL__ < 904+printBind (L _ (AbsBinds {abs_exports = exp})) = +#else+printBind (L _ (XHsBindsLR (AbsBinds {abs_exports = exp}))) = +#endif+ mapM_ (\ e -> liftIO $ putStr $ ((getOccString $ abe_poly e) ++ " ")) exp+printBind _ = return ()+++-- | Computes the variables that are bound by a given piece of syntax.++class HasBV a where+ getBV :: a -> Set Var++instance HasBV (HsBindLR GhcTc GhcTc) where+ getBV (FunBind{fun_id = L _ v}) = Set.singleton v+ getBV (PatBind {pat_lhs = pat}) = getBV pat+ getBV (VarBind {var_id = v}) = Set.singleton v+ getBV PatSynBind{} = Set.empty+#if __GLASGOW_HASKELL__ < 904+ getBV (AbsBinds {abs_exports = es}) = Set.fromList (map abe_poly es)+#else+ getBV (XHsBindsLR (AbsBinds {abs_exports = es})) = Set.fromList (map abe_poly es)+#endif+ +instance HasBV a => HasBV (GenLocated b a) where+ getBV (L _ e) = getBV e++instance HasBV a => HasBV [a] where+ getBV ps = foldl (\s p -> getBV p `Set.union` s) Set.empty ps++#if __GLASGOW_HASKELL__ >= 904+getRecFieldRhs = hfbRHS+#else+getRecFieldRhs = hsRecFieldArg+#endif++getConBV (PrefixCon _ ps) = getBV ps+getConBV (InfixCon p p') = getBV p `Set.union` getBV p'+getConBV (RecCon (HsRecFields {rec_flds = fs})) = foldl run Set.empty fs+ where run s (L _ f) = getBV (getRecFieldRhs f) `Set.union` s++#if __GLASGOW_HASKELL__ < 904+instance HasBV CoPat where+ getBV CoPat {co_pat_inner = p} = getBV p+#else+instance HasBV XXPatGhcTc where+ getBV CoPat {co_pat_inner = p} = getBV p+ getBV (ExpansionPat _ p) = getBV p+#endif++instance HasBV (Pat GhcTc) where+ getBV (VarPat _ (L _ v)) = Set.singleton v+ getBV (LazyPat _ p) = getBV p+#if __GLASGOW_HASKELL__ >= 906+ getBV (AsPat _ (L _ v) _ p) = Set.insert v (getBV p)+#else+ getBV (AsPat _ (L _ v) p) = Set.insert v (getBV p)+#endif+ getBV (BangPat _ p) = getBV p+ getBV (ListPat _ ps) = getBV ps+ getBV (TuplePat _ ps _) = getBV ps+ getBV (SumPat _ p _ _) = getBV p+ getBV (ViewPat _ _ p) = getBV p++ getBV (SplicePat _ sp) =+ case sp of+#if __GLASGOW_HASKELL__ < 906+ HsTypedSplice _ _ v _ -> Set.singleton v+ HsSpliced _ _ (HsSplicedPat p) -> getBV p+ HsUntypedSplice _ _ v _ -> Set.singleton v+ HsQuasiQuote _ p p' _ _ -> Set.fromList [p,p']+ _ -> Set.empty+#else+ HsUntypedSpliceExpr _ e -> getFV e+ HsQuasiQuote _ v _ -> Set.singleton v+#endif++ getBV (NPlusKPat _ (L _ v) _ _ _ _) = Set.singleton v+ getBV (NPat {}) = Set.empty+ getBV (XPat p) = getBV p+ getBV (WildPat {}) = Set.empty+ getBV (LitPat {}) = Set.empty+#if __GLASGOW_HASKELL__ >= 904 + getBV (ParPat _ _ p _) = getBV p+#else+ getBV (ParPat _ p) = getBV p+#endif+ getBV (ConPat {pat_args = con}) = getConBV con+ getBV (SigPat _ p _) = getBV p++#if __GLASGOW_HASKELL__ < 904+instance HasBV NoExtCon where+ getBV _ = Set.empty+#endif++-- | Syntax that may contain variables.+class HasFV a where+ -- | Compute the set of variables occurring in the given piece of+ -- syntax. The name falsely suggests that returns free variables,+ -- but in fact it returns all variable occurrences, no matter+ -- whether they are free or bound.+ getFV :: a -> Set Var ++instance HasFV a => HasFV (GenLocated b a) where+ getFV (L _ e) = getFV e+ +instance HasFV a => HasFV [a] where+ getFV es = foldMap getFV es++instance HasFV a => HasFV (Bag a) where+ getFV es = foldMap getFV es++instance HasFV Var where+ getFV v = Set.singleton v++instance HasFV a => HasFV (MatchGroup GhcTc a) where+ getFV MG {mg_alts = alts} = getFV alts+ +instance HasFV a => HasFV (Match GhcTc a) where+ getFV Match {m_grhss = rhss} = getFV rhss++instance HasFV (HsTupArg GhcTc) where+ getFV (Present _ e) = getFV e+ getFV Missing {} = Set.empty++instance HasFV a => HasFV (GRHS GhcTc a) where+ getFV (GRHS _ g b) = getFV g `Set.union` getFV b++instance HasFV a => HasFV (GRHSs GhcTc a) where+ getFV GRHSs {grhssGRHSs = rhs, grhssLocalBinds = lbs} =+ getFV rhs `Set.union` getFV lbs+++instance HasFV (HsLocalBindsLR GhcTc GhcTc) where+ getFV (HsValBinds _ bs) = getFV bs+ getFV (HsIPBinds _ bs) = getFV bs+ getFV EmptyLocalBinds {} = Set.empty+ +instance HasFV (HsValBindsLR GhcTc GhcTc) where+ getFV (ValBinds _ b _) = getFV b+ getFV (XValBindsLR b) = getFV b++instance HasFV (NHsValBindsLR GhcTc) where+ getFV (NValBinds bs _) = foldMap (getFV . snd) bs++instance HasFV (HsBindLR GhcTc GhcTc) where+ getFV FunBind {fun_matches = ms} = getFV ms+ getFV PatBind {pat_rhs = rhs} = getFV rhs+ getFV VarBind {var_rhs = rhs} = getFV rhs+ getFV PatSynBind {} = Set.empty+#if __GLASGOW_HASKELL__ < 904+ getFV AbsBinds {abs_binds = bs} = getFV bs+#else+ getFV (XHsBindsLR AbsBinds {abs_binds = bs}) = getFV bs+#endif++instance HasFV (IPBind GhcTc) where+ getFV (IPBind _ _ e) = getFV e++instance HasFV (HsIPBinds GhcTc) where+ getFV (IPBinds _ bs) = getFV bs+ +instance HasFV (ApplicativeArg GhcTc) where+ getFV ApplicativeArgOne { arg_expr = e } = getFV e+ getFV ApplicativeArgMany {app_stmts = es, final_expr = e} = getFV es `Set.union` getFV e++instance HasFV (ParStmtBlock GhcTc GhcTc) where+ getFV (ParStmtBlock _ es _ _) = getFV es+ +instance HasFV a => HasFV (StmtLR GhcTc GhcTc a) where+ getFV (LastStmt _ e _ _) = getFV e+ getFV (BindStmt _ _ e) = getFV e+ getFV (ApplicativeStmt _ args _) = foldMap (getFV . snd) args+ getFV (BodyStmt _ e _ _) = getFV e+ getFV (LetStmt _ bs) = getFV bs+ getFV (ParStmt _ stms e _) = getFV stms `Set.union` getFV e+ getFV TransStmt{} = Set.empty -- TODO+ getFV RecStmt{} = Set.empty -- TODO+++instance HasFV (HsRecFields GhcTc (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+ getFV HsRecFields{rec_flds = fs} = getFV fs++#if __GLASGOW_HASKELL__ >= 904+instance HasFV (HsFieldBind o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+#else+instance HasFV (HsRecField' o (GenLocated SrcSpanAnnA (HsExpr GhcTc))) where+#endif+ getFV rf = getFV (getRecFieldRhs rf)++instance HasFV (ArithSeqInfo GhcTc) where+ getFV (From e) = getFV e+ getFV (FromThen e1 e2) = getFV e1 `Set.union` getFV e2+ getFV (FromTo e1 e2) = getFV e1 `Set.union` getFV e2+ getFV (FromThenTo e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+ +#if __GLASGOW_HASKELL__ >= 904+instance HasFV (HsQuote GhcTc) where+#else+instance HasFV (HsBracket GhcTc) where+#endif+ getFV (ExpBr _ e) = getFV e+ getFV (VarBr _ _ e) = getFV e+ getFV _ = Set.empty++instance HasFV (HsCmd GhcTc) where+ getFV (HsCmdArrApp _ e1 e2 _ _) = getFV e1 `Set.union` getFV e2+ getFV (HsCmdArrForm _ e _ _ cmd) = getFV e `Set.union` getFV cmd+ getFV (HsCmdApp _ e1 e2) = getFV e1 `Set.union` getFV e2+ getFV (HsCmdLam _ l) = getFV l+ getFV (HsCmdCase _ _ mg) = getFV mg+ getFV (HsCmdIf _ _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+ getFV (HsCmdDo _ cmd) = getFV cmd+#if __GLASGOW_HASKELL__ >= 904+ getFV (HsCmdPar _ _ cmd _) = getFV cmd+ getFV (HsCmdLet _ _ bs _ _) = getFV bs+#else+ getFV (HsCmdPar _ cmd) = getFV cmd+ getFV (HsCmdLet _ bs _) = getFV bs+#endif+#if __GLASGOW_HASKELL__ >= 904+ getFV (HsCmdLamCase _ _ mg) = getFV mg+#else+ getFV (HsCmdLamCase _ mg) = getFV mg+#endif+ getFV (XCmd e) = getFV e+++instance (HasFV a, HasFV b) => HasFV (Either a b) where+ getFV (Left x) = getFV x+ getFV (Right x) = getFV x++#if __GLASGOW_HASKELL__ >= 908+instance HasFV (LHsRecUpdFields GhcTc) where+ getFV RegularRecUpdFields {recUpdFields = x} = getFV x+ getFV OverloadedRecUpdFields {olRecUpdFields = x} = getFV x+#endif++instance HasFV (HsCmdTop GhcTc) where+ getFV (HsCmdTop _ cmd) = getFV cmd++instance HasFV (HsExpr GhcTc) where+ getFV (HsVar _ v) = getFV v+ getFV HsUnboundVar {} = Set.empty+ getFV HsOverLabel {} = Set.empty+ getFV HsIPVar {} = Set.empty+ getFV HsOverLit {} = Set.empty+ getFV HsLit {} = Set.empty+ getFV (HsLam _ mg) = getFV mg+ getFV (HsApp _ e1 e2) = getFV e1 `Set.union` getFV e2 + getFV (OpApp _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+ getFV (NegApp _ e _) = getFV e+ getFV (SectionL _ e1 e2) = getFV e1 `Set.union` getFV e2+ getFV (SectionR _ e1 e2) = getFV e1 `Set.union` getFV e2+ getFV (ExplicitTuple _ es _) = getFV es+ getFV (ExplicitSum _ _ _ e) = getFV e+ getFV (HsCase _ e mg) = getFV e `Set.union` getFV mg+ getFV (HsMultiIf _ es) = getFV es+ getFV (HsDo _ _ e) = getFV e+ getFV HsProjection {} = Set.empty+ getFV HsGetField {gf_expr = e} = getFV e+ getFV (ExplicitList _ es) = getFV es+ getFV (RecordUpd {rupd_expr = e, rupd_flds = fs}) = getFV e `Set.union` getFV fs+ getFV (RecordCon {rcon_flds = fs}) = getFV fs+ getFV (ArithSeq _ _ e) = getFV e+#if __GLASGOW_HASKELL__ >= 906+ getFV HsTypedSplice{} = Set.empty+ getFV HsUntypedSplice{} = Set.empty+#else+ getFV HsSpliceE{} = Set.empty+#endif+ getFV (HsProc _ _ e) = getFV e+ getFV (HsStatic _ e) = getFV e+ getFV (XExpr e) = getFV e+#if __GLASGOW_HASKELL__ >= 904+ getFV (HsPar _ _ e _) = getFV e + getFV (HsLamCase _ _ mg) = getFV mg+ getFV (HsLet _ _ bs _ e) = getFV bs `Set.union` getFV e+ getFV HsRecSel {} = Set.empty+ getFV (HsTypedBracket _ e) = getFV e+ getFV (HsUntypedBracket _ e) = getFV e+#else + getFV (HsBinTick _ _ _ e) = getFV e+ getFV (HsTick _ _ e) = getFV e+ getFV (HsLet _ bs e) = getFV bs `Set.union` getFV e+ getFV (HsPar _ e) = getFV e+ getFV (HsLamCase _ mg) = getFV mg+ getFV HsConLikeOut {} = Set.empty+ getFV HsRecFld {} = Set.empty+ getFV (HsBracket _ e) = getFV e+ getFV HsRnBracketOut {} = Set.empty+ getFV HsTcBracketOut {} = Set.empty+#endif++#if __GLASGOW_HASKELL__ >= 906+ getFV (HsAppType _ e _ _) = getFV e+ getFV (ExprWithTySig _ e _) = getFV e +#else+ getFV (HsAppType _ e _) = getFV e+ getFV (ExprWithTySig _ e _) = getFV e +#endif+ getFV (HsIf _ e1 e2 e3) = getFV e1 `Set.union` getFV e2 `Set.union` getFV e3+ getFV (HsPragE _ _ e) = getFV e+++instance HasFV XXExprGhcTc where+ getFV (WrapExpr e) = getFV e+ getFV (ExpansionExpr (HsExpanded _e1 e2)) = getFV e2+#if __GLASGOW_HASKELL__ >= 904 + getFV (HsTick _ e) = getFV e+ getFV (HsBinTick _ _ e) = getFV e+ getFV ConLikeTc{} = Set.empty+#endif+++instance HasFV (e GhcTc) => HasFV (HsWrap e) where+ getFV (HsWrap _ e) = getFV e
+ src/WidgetRattus/Plugin/PrimExpr.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE OverloadedStrings #-}++module WidgetRattus.Plugin.PrimExpr (+ Prim (..),+ PrimInfo (..),+ function,+ prim,+ isPrimExpr+) where++import Data.Map (Map)+import qualified Data.Map as Map+import GHC.Plugins+import WidgetRattus.Plugin.Utils+import Prelude hiding ((<>))++data Prim = Delay | Adv | Box | Select++-- DelayApp has the following fields: Var = delay f, T1 = value type, T2 = later type (O v a)+-- AdvApp has the following fields: Var = adv f, TypedArg = var and type for arg+data PrimInfo = DelayApp Var Type | AdvApp Var TypedArg | BoxApp Var | SelectApp Var TypedArg TypedArg++type TypedArg = (Var, Type)++data PartialPrimInfo = PartialPrimInfo {+ primPart :: Prim,+ functionPart :: Var,+ args :: [Var],+ typeArgs :: [Type]+}++instance Outputable PartialPrimInfo where+ ppr (PartialPrimInfo Delay f _ typeArgs) = text "PartialPrimInfo { prim = Delay, function = " <> ppr f <> text "args = (not printing since it should be undefined) , typeArgs = " <> ppr typeArgs + ppr (PartialPrimInfo prim f args typeArgs) = text "PartialPrimInfo { prim = " <> ppr prim <> text ", function = " <> ppr f <> text ", args = " <> ppr args <> text ", typeArgs = " <> ppr typeArgs++instance Outputable Prim where+ ppr Delay = "delay"+ ppr Adv = "adv"+ ppr Select = "select"+ ppr Box = "box"++instance Outputable PrimInfo where+ ppr (DelayApp f _) = text "DelayApp - function " <> ppr f + ppr (BoxApp f) = text "BoxApp - function " <> ppr f+ ppr (AdvApp f arg) = text "AdvApp - function " <> ppr f <> text " | arg " <> ppr arg+ ppr (SelectApp f arg arg2) = text "SelectApp - function " <> ppr f <> text " | arg " <> ppr arg <> text " | arg2 " <> ppr arg2+ +primMap :: Map FastString Prim+primMap = Map.fromList+ [("delay", Delay),+ ("adv", Adv),+ ("select", Select),+ ("box", Box)+ ]+++isPrim :: Var -> Maybe Prim+isPrim v = case getNameModule v of+ Just (name, mod) | isRattModule mod -> Map.lookup name primMap+ _ -> Nothing++createPartialPrimInfo :: Prim -> Var -> PartialPrimInfo+createPartialPrimInfo prim function =+ PartialPrimInfo {+ primPart = prim,+ functionPart = function,+ args = [],+ typeArgs = []+ }++function :: PrimInfo -> Var+function (DelayApp f _) = f+function (BoxApp f) = f+function (AdvApp f _) = f+function (SelectApp f _ _) = f++prim :: PrimInfo -> Prim+prim (DelayApp {}) = Delay+prim (BoxApp _) = Box+prim (AdvApp {}) = Adv+prim (SelectApp {}) = Select++validatePartialPrimInfo :: PartialPrimInfo -> Maybe PrimInfo+validatePartialPrimInfo (PartialPrimInfo Select f [arg2V, argV] [arg2T, argT]) = Just $ SelectApp f (argV, argT) (arg2V, arg2T)+validatePartialPrimInfo (PartialPrimInfo Delay f [_] [argT]) = Just $ DelayApp f argT+validatePartialPrimInfo (PartialPrimInfo {primPart = Box, functionPart = f}) = Just $ BoxApp f+validatePartialPrimInfo (PartialPrimInfo Adv f [argV] [argT]) = Just $ AdvApp f (argV, argT)+validatePartialPrimInfo _ = Nothing++isPrimExpr :: Expr Var -> Maybe PrimInfo+isPrimExpr expr = isPrimExpr' expr >>= validatePartialPrimInfo++isPrimExpr' :: Expr Var -> Maybe PartialPrimInfo+isPrimExpr' (App e (Type t)) = case mPPI of+ Just pPI@(PartialPrimInfo {typeArgs = tArgs}) -> Just pPI {typeArgs = t : tArgs}+ Nothing -> Nothing+ where mPPI = isPrimExpr' e+isPrimExpr' (App e e') =+ case isPrimExpr' e of+ Just partPrimInfo@(PartialPrimInfo { primPart = Delay, args = args}) -> Just partPrimInfo {args = undefined : args}+ Just partPrimInfo@(PartialPrimInfo { args = args}) -> Just partPrimInfo {args = maybe args (:args) (getMaybeVar e')}+ _ -> Nothing+isPrimExpr' (Var v) = case isPrim v of+ Just p -> Just $ createPartialPrimInfo p v+ Nothing -> Nothing+isPrimExpr' (Tick _ e) = isPrimExpr' e+isPrimExpr' (Lam v e)+ | isTyVar v || (not $ tcIsLiftedTypeKind $ typeKind $ varType v) = isPrimExpr' e+isPrimExpr' _ = Nothing
+ src/WidgetRattus/Plugin/ScopeCheck.hs view
@@ -0,0 +1,812 @@+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ImplicitParams #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE CPP #-}++++-- | This module implements the source plugin that checks the variable+-- scope of of Async Rattus programs.++module WidgetRattus.Plugin.ScopeCheck (checkAll) where++import WidgetRattus.Plugin.Utils+import WidgetRattus.Plugin.Dependency+import WidgetRattus.Plugin.Annotation++import Control.Monad.Trans.State.Strict+import Data.IORef++import Prelude hiding ((<>))++import GHC.Parser.Annotation+import GHC.Plugins+import GHC.Tc.Types+import GHC.Data.Bag+import GHC.Tc.Types.Evidence+import GHC.Hs.Extension+import GHC.Hs.Expr+import GHC.Hs.Pat+import GHC.Hs.Binds++import Data.Graph+import qualified Data.Set as Set+import qualified Data.Map as Map+import Data.Set (Set)+import Data.Map (Map)+import Data.List+import Data.List.NonEmpty (NonEmpty(..),(<|),nonEmpty)+import System.Exit+import Data.Either+import Data.Maybe++import Data.Data hiding (tyConName)++import Control.Monad++type ErrorMsg = (Severity,SrcSpan,SDoc)+type ErrorMsgsRef = IORef [ErrorMsg]++-- | The current context for scope checking+data Ctxt = Ctxt+ {+ errorMsgs :: ErrorMsgsRef,+ -- | Variables that are in scope now (i.e. occurring in the typing+ -- context but not to the left of a tick)+ current :: LCtxt,+ -- | Variables that are in the typing context, but to the left of a+ -- tick+ earlier :: Either NoTickReason (NonEmpty LCtxt),+ -- | Variables that have fallen out of scope. The map contains the+ -- reason why they have fallen out of scope.+ hidden :: Hidden,+ -- -- | Same as 'hidden' but for recursive variables.+ -- hiddenRec :: Hidden,+ -- | The current location information.+ srcLoc :: SrcSpan,+ -- | If we are in the body of a recursively defined function, this+ -- field contains the variables that are defined recursively+ -- (could be more than one due to mutual recursion or because of a+ -- recursive pattern definition) and the location of the recursive+ -- definition.+ recDef :: Maybe RecDef,+ -- | Type variables with a 'Stable' constraint attached to them.+ stableTypes :: Set Var,+ -- | A mapping from variables to the primitives that they are+ -- defined equal to. For example, a program could contain @let+ -- mydel = delay in mydel 1@, in which case @mydel@ is mapped to+ -- 'Delay'.+ primAlias :: Map Var Prim,+ -- | Allow general recursion.+ allowRecursion :: Bool}++++-- | The starting context for checking a top-level definition. For+-- non-recursive definitions, the argument is @Nothing@. Otherwise, it+-- contains the recursively defined variables along with the location+-- of the recursive definition.+emptyCtxt :: ErrorMsgsRef -> Maybe (Set Var,SrcSpan) -> Bool -> Ctxt+emptyCtxt em mvar allowRec =+ Ctxt { errorMsgs = em,+ current = Set.empty,+ earlier = Left NoDelay,+ hidden = Map.empty,+ srcLoc = noLocationInfo,+ recDef = mvar,+ primAlias = Map.empty,+ stableTypes = Set.empty,+ allowRecursion = allowRec}++-- | A local context, consisting of a set of variables.+type LCtxt = Set Var++-- | The recursively defined variables + the position where the+-- recursive definition starts+type RecDef = (Set Var, SrcSpan)+++++data StableReason = StableRec SrcSpan | StableBox deriving Show++-- | Indicates, why a variable has fallen out of scope.+data HiddenReason = Stabilize StableReason | FunDef | DelayApp | AdvApp | SelectApp deriving Show++-- | Indicates, why there is no tick+data NoTickReason = NoDelay | TickHidden HiddenReason deriving Show++-- | Hidden context, containing variables that have fallen out of+-- context along with the reason why they have.+type Hidden = Map Var HiddenReason++-- | The 5 primitive Asynchronous Rattus operations.+data Prim = Delay | Adv | Select | Box | Unbox deriving Show++-- | This constraint is used to pass along the context implicitly via+-- an implicit parameter.+type GetCtxt = ?ctxt :: Ctxt+++type CheckM = StateT ([Maybe (Prim, SrcSpan)]) TcM++-- | This type class is implemented for each AST type @a@ for which we+-- can check whether it adheres to the scoping rules of Asynchronous Rattus.+class Scope a where+ -- | Check whether the argument is a scope correct piece of syntax+ -- in the given context.+ check :: GetCtxt => a -> CheckM Bool++-- | This is a variant of 'Scope' for syntax that can also bind+-- variables.+class ScopeBind a where+ -- | 'checkBind' checks whether its argument is scope-correct and in+ -- addition returns the the set of variables bound by it.+ checkBind :: GetCtxt => a -> CheckM (Bool,Set Var)+++-- | set the current context.+setCtxt :: Ctxt -> (GetCtxt => a) -> a +setCtxt c a = let ?ctxt = c in a+++-- | modify the current context.+modifyCtxt :: (Ctxt -> Ctxt) -> (GetCtxt => a) -> (GetCtxt => a)+modifyCtxt f a =+ let newc = f ?ctxt in+ let ?ctxt = newc in a+++++getLocAnn' :: SrcSpanAnn' b -> SrcSpan+getLocAnn' = locA+++updateLoc :: SrcSpanAnn' b -> (GetCtxt => a) -> (GetCtxt => a)+updateLoc src = modifyCtxt (\c -> c {srcLoc = getLocAnn' src})+++-- | Check all definitions in the given module. If Scope errors are+-- found, the current execution is halted with 'exitFailure'.+checkAll :: TcGblEnv -> TcM ()+checkAll env = do+ let bindDep = dependency (tcg_binds env)+ result <- mapM (checkSCC' (tcg_mod env) (tcg_ann_env env)) bindDep+ let (res,msgs) = foldl' (\(b,l) (b',l') -> (b && b', l ++ l')) (True,[]) result+ printAccErrMsgs msgs+ if res then return () else liftIO exitFailure+++printAccErrMsgs :: [ErrorMsg] -> TcM ()+printAccErrMsgs msgs = mapM_ printMsg (sortOn (\(_,l,_)->l) msgs)+ where printMsg (sev,loc,doc) = printMessage sev loc doc+++++instance Scope a => Scope (GenLocated SrcSpan a) where+ check (L l x) = (\c -> c {srcLoc = l}) `modifyCtxt` check x++instance Scope a => Scope (GenLocated (SrcSpanAnn' b) a) where+ check (L l x) = updateLoc l $ check x+ +instance Scope a => Scope (Bag a) where+ check bs = fmap and (mapM check (bagToList bs))++instance Scope a => Scope [a] where+ check ls = fmap and (mapM check ls)+++instance Scope (Match GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+ check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs++instance Scope (Match GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+ check Match{m_pats=ps,m_grhss=rhs} = addVars (getBV ps) `modifyCtxt` check rhs+++instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+ check MG {mg_alts = alts} = check alts+++instance Scope (MatchGroup GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+ check MG {mg_alts = alts} = check alts+++instance Scope a => ScopeBind (StmtLR GhcTc GhcTc a) where+ checkBind (LastStmt _ b _ _) = ( , Set.empty) <$> check b+ checkBind (BindStmt _ p b) = do+ let vs = getBV p+ let c' = addVars vs ?ctxt+ r <- setCtxt c' (check b)+ return (r,vs)+ checkBind (BodyStmt _ b _ _) = ( , Set.empty) <$> check b+ checkBind (LetStmt _ bs) = checkBind bs+ checkBind ParStmt{} = notSupported "monad comprehensions"+ checkBind TransStmt{} = notSupported "monad comprehensions"+ checkBind ApplicativeStmt{} = notSupported "applicative do notation"+ checkBind RecStmt{} = notSupported "recursive do notation"++instance ScopeBind a => ScopeBind [a] where+ checkBind [] = return (True,Set.empty)+ checkBind (x:xs) = do+ (r,vs) <- checkBind x+ (r',vs') <- addVars vs `modifyCtxt` (checkBind xs)+ return (r && r',vs `Set.union` vs')++instance ScopeBind a => ScopeBind (GenLocated SrcSpan a) where+ checkBind (L l x) = (\c -> c {srcLoc = l}) `modifyCtxt` checkBind x++instance ScopeBind a => ScopeBind (GenLocated (SrcSpanAnn' b) a) where+ checkBind (L l x) = updateLoc l $ checkBind x++instance Scope a => Scope (GRHS GhcTc a) where+ check (GRHS _ gs b) = do+ (r, vs) <- checkBind gs+ r' <- addVars vs `modifyCtxt` (check b)+ return (r && r')++checkRec :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool+checkRec b = liftM2 (&&) (checkPatBind b) (check b)++checkPatBind :: GetCtxt => LHsBindLR GhcTc GhcTc -> CheckM Bool+checkPatBind (L l b) = updateLoc l $ checkPatBind' b++checkPatBind' :: GetCtxt => HsBindLR GhcTc GhcTc -> CheckM Bool+checkPatBind' PatBind{} = do+ printMessage' SevError ("(Mutual) recursive pattern binding definitions are not supported in Asynchronous Rattus")+ return False+#if __GLASGOW_HASKELL__ < 904+checkPatBind' AbsBinds {abs_binds = binds} = +#else+checkPatBind' (XHsBindsLR AbsBinds {abs_binds = binds}) = +#endif+ liftM and (mapM checkPatBind (bagToList binds))++checkPatBind' _ = return True+++-- | Check the scope of a list of (mutual) recursive bindings. The+-- second argument is the set of variables defined by the (mutual)+-- recursive bindings+checkRecursiveBinds :: GetCtxt => [LHsBindLR GhcTc GhcTc] -> Set Var -> CheckM (Bool, Set Var)+checkRecursiveBinds bs vs = do+ res <- fmap and (mapM check' bs)+ return (res, vs)+ where check' b@(L l _) = fc (getLocAnn' l) `modifyCtxt` checkRec b+ fc l c = let+ ctxHid = either (const $ current c) (Set.union (current c) . Set.unions) (earlier c)+ in c {current = Set.empty,+ earlier = Left (TickHidden $ Stabilize $ StableRec l),+ hidden = hidden c `Map.union`+ (Map.fromSet (const (Stabilize (StableRec l))) ctxHid),+ recDef = maybe (Just (vs,l)) (\(vs',_) -> Just (Set.union vs' vs,l)) (recDef c)+ -- TODO fix location info of recDef (needs one location for each var)+ } +++instance ScopeBind (SCC (GenLocated SrcSpanAnnA (HsBindLR GhcTc GhcTc), Set Var)) where+ checkBind (AcyclicSCC (b,vs)) = (, vs) <$> check b+ checkBind (CyclicSCC bs) = checkRecursiveBinds (map fst bs) (foldMap snd bs)+ +instance ScopeBind (HsValBindsLR GhcTc GhcTc) where+ checkBind (ValBinds _ bs _) = checkBind (dependency bs)+ + checkBind (XValBindsLR (NValBinds binds _)) = checkBind binds+++instance ScopeBind (HsBindLR GhcTc GhcTc) where+ checkBind b = (, getBV b) <$> check b+++-- | Compute the set of variables defined by the given Haskell binder.+getAllBV :: GenLocated l (HsBindLR GhcTc GhcTc) -> Set Var+getAllBV (L _ b) = getAllBV' b where+ getAllBV' (FunBind{fun_id = L _ v}) = Set.singleton v+#if __GLASGOW_HASKELL__ < 904+ getAllBV' (AbsBinds {abs_exports = es, abs_binds = bs}) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs+ getAllBV' XHsBindsLR{} = Set.empty+#else+ getAllBV' (XHsBindsLR (AbsBinds {abs_exports = es, abs_binds = bs})) = Set.fromList (map abe_poly es) `Set.union` foldMap getBV bs+#endif+ getAllBV' (PatBind {pat_lhs = pat}) = getBV pat+ getAllBV' (VarBind {var_id = v}) = Set.singleton v+ getAllBV' PatSynBind{} = Set.empty+++-- Check nested bindings+instance ScopeBind (RecFlag, Bag (GenLocated SrcSpanAnnA (HsBindLR GhcTc GhcTc))) where+ checkBind (NonRecursive, bs) = checkBind $ bagToList bs+ checkBind (Recursive, bs) = checkRecursiveBinds bs' (foldMap getAllBV bs')+ where bs' = bagToList bs+++instance ScopeBind (HsLocalBindsLR GhcTc GhcTc) where+ checkBind (HsValBinds _ bs) = checkBind bs+ checkBind HsIPBinds {} = notSupported "implicit parameters"+ checkBind EmptyLocalBinds{} = return (True,Set.empty)++type SrcAnno = SrcSpanAnnA+ +instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsExpr GhcTc))) where+ check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do+ (l,vs) <- checkBind lbinds+ r <- addVars vs `modifyCtxt` (check rhs)+ return (r && l)++instance Scope (GRHSs GhcTc (GenLocated SrcAnno (HsCmd GhcTc))) where+ check GRHSs{grhssGRHSs = rhs, grhssLocalBinds = lbinds} = do+ (l,vs) <- checkBind lbinds+ r <- addVars vs `modifyCtxt` (check rhs)+ return (r && l)++instance Show Var where+ show v = getOccString v+++tickHidden :: HiddenReason -> SDoc+tickHidden FunDef = "a function definition"+tickHidden DelayApp = "a nested application of delay"+tickHidden AdvApp = "an application of adv"+tickHidden SelectApp = "an application of select"+tickHidden (Stabilize StableBox) = "an application of box"+tickHidden (Stabilize (StableRec src)) = "a nested recursive definition (at " <> ppr src <> ")"++isSelect :: GetCtxt => LHsExpr GhcTc -> Bool+isSelect e =+ case isPrimExpr e of+ Just (Select, _) -> True+ _ -> False++instance Scope (HsExpr GhcTc) where+ check (HsVar _ (L _ v))+ | Just p <- isPrim v =+ case p of+ Unbox -> return True+ _ -> printMessageCheck SevError ("Defining an alias for " <> ppr v <> " is not allowed")+ | otherwise = case getScope v of+ Hidden reason -> printMessageCheck SevError reason+ Visible -> return True+ ImplUnboxed -> return True+ -- printMessageCheck SevWarning+ -- (ppr v <> text " is an external temporal function used under delay, which may cause time leaks.")+ check (HsApp _ (L _ (HsApp _ f arg)) arg2) | isSelect f =+ case earlier ?ctxt of+ Right (er :| ers) -> do+ res <- get+ case res of+ Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")+ Nothing : pre -> do put pre+ b1 <- mod `modifyCtxt` check arg+ b2 <- mod `modifyCtxt` check arg2+ modify (Just (Select, srcLoc ?ctxt) :)+ return $ b1 && b2+ _ -> error "Asynchronous Rattus: internal error"+ where mod c = c{earlier = case nonEmpty ers of+ Nothing -> Left $ TickHidden SelectApp+ Just ers' -> Right ers',+ current = er,+ hidden = hidden ?ctxt `Map.union`+ Map.fromSet (const SelectApp) (current ?ctxt)}+ Left NoDelay -> printMessageCheck SevError "select may only be used in the scope of a delay."+ Left (TickHidden hr) -> printMessageCheck SevError ("select may only be used in the scope of a delay. "+ <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+ check (HsApp _ e1 e2) =+ case isPrimExpr e1 of+ Just (p,_) -> case p of+ Box -> do+ ch <- stabilize StableBox `modifyCtxt` check e2+ return ch+ Unbox -> check e2+ Delay -> do modify (Nothing :)+ b <- (\c -> c{current = Set.empty,+ earlier = case earlier c of+ Left _ -> Right (current c :| [])+ Right cs -> Right (current c <| cs)})+ `modifyCtxt` check e2+ res <- get+ case res of+ Nothing : _ -> printMessageCheck SevError "No adv or select found in the scope of this occurrence of delay"+ _ : pre -> put pre >> return b+ _ -> error "Asynchronous Rattus: internal error"+ Adv -> case earlier ?ctxt of+ Right (er :| ers) -> do+ res <- get+ case res of+ Just _ : _ -> printMessageCheck SevError ("only one adv or select may be used in the scope of a delay.")+ Nothing : pre -> do put pre+ b <- mod `modifyCtxt` check e2+ modify (Just (Adv,srcLoc ?ctxt) :)+ return b+ _ -> error "Asynchronous Rattus: internal error"+ where mod c = c{earlier = case nonEmpty ers of+ Nothing -> Left $ TickHidden AdvApp+ Just ers' -> Right ers',+ current = er,+ hidden = hidden ?ctxt `Map.union`+ Map.fromSet (const AdvApp) (current ?ctxt)}+ Left NoDelay -> printMessageCheck SevError ("adv may only be used in the scope of a delay.")+ Left (TickHidden hr) -> printMessageCheck SevError ("adv may only be used in the scope of a delay. "+ <> " There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+ Select -> printMessageCheck SevError ("select must be fully applied")+ _ -> liftM2 (&&) (check e1) (check e2)+ check HsUnboundVar{} = return True+#if __GLASGOW_HASKELL__ >= 904+ check (HsPar _ _ e _) = check e+ check (HsLamCase _ _ mg) = check mg+ check HsRecSel{} = return True+ check HsTypedBracket{} = notSupported "MetaHaskell"+ check HsUntypedBracket{} = notSupported "MetaHaskell"+#else+ check HsConLikeOut{} = return True+ check HsRecFld{} = return True+ check (HsPar _ e) = check e+ check (HsLamCase _ mg) = check mg+ check HsBracket{} = notSupported "MetaHaskell"+ check (HsTick _ _ e) = check e+ check (HsBinTick _ _ _ e) = check e+ check HsRnBracketOut{} = notSupported "MetaHaskell"+ check HsTcBracketOut{} = notSupported "MetaHaskell"+#endif+#if __GLASGOW_HASKELL__ >= 904+ check (HsLet _ _ bs _ e) = do+#else+ check (HsLet _ bs e) = do+#endif+ (l,vs) <- checkBind bs+ r <- addVars vs `modifyCtxt` (check e)+ return (r && l)+ + check HsOverLabel{} = return True+ check HsIPVar{} = notSupported "implicit parameters"+ check HsOverLit{} = return True + check HsLit{} = return True+ check (OpApp _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]+ check (HsLam _ mg) = check mg+ check (HsCase _ e1 e2) = (&&) <$> check e1 <*> check e2+ check (SectionL _ e1 e2) = (&&) <$> check e1 <*> check e2+ check (SectionR _ e1 e2) = (&&) <$> check e1 <*> check e2+ check (ExplicitTuple _ e _) = check e+ check (NegApp _ e _) = check e+ check (ExplicitSum _ _ _ e) = check e+ check (HsMultiIf _ e) = check e+ check (ExplicitList _ e) = check e+ check HsProjection {} = return True+ check HsGetField {gf_expr = e} = check e+ check RecordUpd { rupd_expr = e, rupd_flds = fs} = (&&) <$> check e <*> check fs+ check RecordCon { rcon_flds = f} = check f+ check (ArithSeq _ _ e) = check e+#if __GLASGOW_HASKELL__ >= 906+ check HsTypedSplice{} = notSupported "Template Haskell"+ check HsUntypedSplice{} = notSupported "Template Haskell"+#else+ check HsSpliceE{} = notSupported "Template Haskell"+#endif+ check (HsProc _ _ e) = check e+ check (HsStatic _ e) = check e+ check (HsDo _ _ e) = fst <$> checkBind e+ check (XExpr e) = check e+#if __GLASGOW_HASKELL__ >= 906+ check (HsAppType _ e _ _) = check e+ check (ExprWithTySig _ e _) = check e+#else+ check (HsAppType _ e _) = check e+ check (ExprWithTySig _ e _) = check e+#endif+ check (HsPragE _ _ e) = check e+ check (HsIf _ e1 e2 e3) = and <$> mapM check [e1,e2,e3]+++instance (Scope a, Scope b) => Scope (Either a b) where+ check (Left x) = check x+ check (Right x) = check x+++#if __GLASGOW_HASKELL__ >= 908+instance Scope (LHsRecUpdFields GhcTc) where+ check RegularRecUpdFields {recUpdFields = x} = check x+ check OverloadedRecUpdFields {olRecUpdFields = x} = check x+#endif+++instance Scope XXExprGhcTc where+ check (WrapExpr (HsWrap _ e)) = check e+ check (ExpansionExpr (HsExpanded _ e)) = check e+#if __GLASGOW_HASKELL__ >= 904+ check ConLikeTc{} = return True+ check (HsTick _ e) = check e+ check (HsBinTick _ _ e) = check e+#endif++instance Scope (HsCmdTop GhcTc) where+ check (HsCmdTop _ e) = check e+ +instance Scope (HsCmd GhcTc) where+ check (HsCmdArrApp _ e1 e2 _ _) = (&&) <$> check e1 <*> check e2+ check (HsCmdDo _ e) = fst <$> checkBind e+ check (HsCmdArrForm _ e1 _ _ e2) = (&&) <$> check e1 <*> check e2+ check (HsCmdApp _ e1 e2) = (&&) <$> check e1 <*> check e2+ check (HsCmdLam _ e) = check e+#if __GLASGOW_HASKELL__ >= 904+ check (HsCmdPar _ _ e _) = check e+ check (HsCmdLamCase _ _ e) = check e + check (HsCmdLet _ _ bs _ e) = do+#else+ check (HsCmdPar _ e) = check e+ check (HsCmdLamCase _ e) = check e+ check (HsCmdLet _ bs e) = do+#endif+ (l,vs) <- checkBind bs+ r <- addVars vs `modifyCtxt` (check e)+ return (r && l)++ check (HsCmdCase _ e1 e2) = (&&) <$> check e1 <*> check e2+ check (HsCmdIf _ _ e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3+ check (XCmd (HsWrap _ e)) = check e+++instance Scope (ArithSeqInfo GhcTc) where+ check (From e) = check e+ check (FromThen e1 e2) = (&&) <$> check e1 <*> check e2+ check (FromTo e1 e2) = (&&) <$> check e1 <*> check e2+ check (FromThenTo e1 e2 e3) = (&&) <$> ((&&) <$> check e1 <*> check e2) <*> check e3++instance Scope a => Scope (HsRecFields GhcTc a) where+ check HsRecFields {rec_flds = fs} = check fs++++#if __GLASGOW_HASKELL__ >= 904+instance Scope b => Scope (HsFieldBind a b) where+ check HsFieldBind{hfbRHS = a} = check a+#else+instance Scope b => Scope (HsRecField' a b) where+ check HsRecField{hsRecFieldArg = a} = check a+#endif++instance Scope (HsTupArg GhcTc) where+ check (Present _ e) = check e+ check Missing{} = return True++instance Scope (HsBindLR GhcTc GhcTc) where+#if __GLASGOW_HASKELL__ >= 904+ check (XHsBindsLR AbsBinds {abs_binds = binds, abs_ev_vars = ev})+#else+ check AbsBinds {abs_binds = binds, abs_ev_vars = ev}+#endif+ = mod `modifyCtxt` check binds+ where mod c = c { stableTypes= stableTypes c `Set.union`+ Set.fromList (mapMaybe (isStableConstr . varType) ev)}+ check FunBind{fun_matches= matches, fun_id = L _ v,+ fun_ext = wrapper} =+ mod `modifyCtxt` check matches+ where mod c = c { stableTypes= stableTypes c `Set.union`+ Set.fromList (stableConstrFromWrapper' wrapper) `Set.union`+ Set.fromList (extractStableConstr (varType v))}+ check PatBind{pat_lhs = lhs, pat_rhs=rhs} = addVars (getBV lhs) `modifyCtxt` check rhs+ check VarBind{var_rhs = rhs} = check rhs+ check PatSynBind {} = return True -- pattern synonyms are not supported+++-- | Checks whether the given type is a type constraint of the form+-- @Stable a@ for some type variable @a@. In that case it returns the+-- type variable @a@.+isStableConstr :: Type -> Maybe TyVar+isStableConstr t = + case splitTyConApp_maybe t of+ Just (con,[args]) ->+ case getNameModule con of+ Just (name, mod) ->+ if isRattModule mod && name == "Stable"+ then (getTyVar_maybe args)+ else Nothing+ _ -> Nothing + _ -> Nothing++++#if __GLASGOW_HASKELL__ >= 906+stableConstrFromWrapper' :: (HsWrapper , a) -> [TyVar]+stableConstrFromWrapper' (x , _) = stableConstrFromWrapper x+#else+stableConstrFromWrapper' :: HsWrapper -> [TyVar]+stableConstrFromWrapper' = stableConstrFromWrapper+#endif++stableConstrFromWrapper :: HsWrapper -> [TyVar]+stableConstrFromWrapper (WpCompose v w) = stableConstrFromWrapper v ++ stableConstrFromWrapper w+stableConstrFromWrapper (WpEvLam v) = maybeToList $ isStableConstr (varType v)+stableConstrFromWrapper _ = []+++-- | Given a type @(C1, ... Cn) => t@, this function returns the list+-- of type variables @[a1,...,am]@ for which there is a constraint+-- @Stable ai@ among @C1, ... Cn@.+extractStableConstr :: Type -> [TyVar]+extractStableConstr = mapMaybe isStableConstr . map irrelevantMult . fst . splitFunTys . snd . splitForAllTys'+++getSCCLoc :: SCC (LHsBindLR GhcTc GhcTc, Set Var) -> SrcSpan+getSCCLoc (AcyclicSCC (L l _ ,_)) = getLocAnn' l+getSCCLoc (CyclicSCC ((L l _,_ ) : _)) = getLocAnn' l+getSCCLoc _ = noLocationInfo++checkSCC' :: Module -> AnnEnv -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> TcM (Bool, [ErrorMsg])+checkSCC' mod anEnv scc = do+ err <- liftIO (newIORef [])+ let allowRec = AllowRecursion `Set.member` getAnn mod anEnv scc+ res <- checkSCC allowRec err scc+ msgs <- liftIO (readIORef err)+ let anns = getAnn mod anEnv scc+ if ExpectWarning `Set.member` anns + then if ExpectError `Set.member` anns+ then return (False,[(SevError, getSCCLoc scc, "Annotation to expect both warning and error is not allowed.")])+ else if any (\(s,_,_) -> case s of SevWarning -> True; _ -> False) msgs+ then return (res, filter (\(s,_,_) -> case s of SevWarning -> False; _ -> True) msgs)+ else return (False,[(SevError, getSCCLoc scc, "Warning was expected, but typechecking produced no warning.")])+ else if ExpectError `Set.member` anns+ then if res+ then return (False,[(SevError, getSCCLoc scc, "Error was expected, but typechecking produced no error.")])+ else return (True,[])+ else return (res, msgs)+getAnn :: forall a . (Data a, Ord a) => Module -> AnnEnv -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> Set a+getAnn mod anEnv scc =+ case scc of+ (AcyclicSCC (_,vs)) -> Set.unions $ Set.map checkVar vs+ (CyclicSCC bs) -> Set.unions $ map (Set.unions . Set.map checkVar . snd) bs+ where checkVar :: Var -> Set a+ checkVar v =+ let anns = findAnns deserializeWithData anEnv (NamedTarget name) :: [a]+ annsMod = findAnns deserializeWithData anEnv (ModuleTarget mod) :: [a]+ name :: Name+ name = varName v+ in Set.fromList anns `Set.union` Set.fromList annsMod++++-- | Checks a top-level definition group, which is either a single+-- non-recursive definition or a group of (mutual) recursive+-- definitions.++checkSCC :: Bool -> ErrorMsgsRef -> SCC (LHsBindLR GhcTc GhcTc, Set Var) -> TcM Bool+checkSCC allowRec errm (AcyclicSCC (b,_)) = setCtxt (emptyCtxt errm Nothing allowRec) (evalStateT (check b) [])++checkSCC allowRec errm (CyclicSCC bs) = (fmap and (mapM check' bs'))+ where bs' = map fst bs+ vs = foldMap snd bs+ check' b@(L l _) = setCtxt (emptyCtxt errm (Just (vs,getLocAnn' l)) allowRec) (evalStateT (checkRec b) [])++-- | Stabilizes the given context, i.e. remove all non-stable types+-- and any tick. This is performed on checking 'box', and+-- guarded recursive definitions. To provide better error messages a+-- reason has to be given as well.+stabilize :: StableReason -> Ctxt -> Ctxt+stabilize sr c = c+ {current = Set.empty,+ earlier = Left $ TickHidden hr,+ hidden = hidden c `Map.union` Map.fromSet (const hr) ctxHid}+ where ctxHid = either (const $ current c) (foldl' Set.union (current c)) (earlier c)+ hr = Stabilize sr++data VarScope = Hidden SDoc | Visible | ImplUnboxed+++-- | This function checks whether the given variable is in scope.+getScope :: GetCtxt => Var -> VarScope+getScope v =+ case ?ctxt of+ Ctxt{recDef = Just (vs,_), earlier = e, allowRecursion = allowRec} | v `Set.member` vs ->+ if allowRec then Visible else+ case e of+ Right _ -> Visible+ Left NoDelay -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay")+ Left (TickHidden hr) -> Hidden ("The (mutually) recursive call to " <> ppr v <> " must occur in the scope of a delay. "+ <> "There is a delay, but its scope is interrupted by " <> tickHidden hr <> ".")+ _ -> case Map.lookup v (hidden ?ctxt) of+ Just (Stabilize (StableRec rv)) ->+ if (isStable (stableTypes ?ctxt) (varType v)) || allowRecursion ?ctxt then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+ "It appears in a local recursive definition (at " <> ppr rv <> ")"+ $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ Just (Stabilize StableBox) ->+ if (isStable (stableTypes ?ctxt) (varType v)) then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+ "It occurs under " <> keyword "box" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ Just AdvApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under adv.")+ Just SelectApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs under select.")+ Just DelayApp -> Hidden ("Variable " <> ppr v <> " is no longer in scope due to repeated application of delay")+ Just FunDef -> if (isStable (stableTypes ?ctxt) (varType v)) then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope: It occurs in a function that is defined under a delay, is a of a non-stable type " <> ppr (varType v) <> ", and is bound outside delay")+ Nothing+ | either (const False) (any (Set.member v)) (earlier ?ctxt) ->+ if isStable (stableTypes ?ctxt) (varType v) then Visible+ else Hidden ("Variable " <> ppr v <> " is no longer in scope:" $$+ "It occurs under delay" $$ "and is of type " <> ppr (varType v) <> ", which is not stable.")+ | Set.member v (current ?ctxt) -> Visible+ | isTemporal (varType v) && isRight (earlier ?ctxt) && userFunction v+ -> ImplUnboxed+ | otherwise -> Visible++-- | A map from the syntax of a primitive of Asynchronous Rattus to 'Prim'.+primMap :: Map FastString Prim+primMap = Map.fromList+ [("Delay", Delay),+ ("delay", Delay),+ ("adv", Adv),+ ("select", Select),+ ("box", Box),+ ("unbox", Unbox)]+++-- | Checks whether a given variable is in fact an Asynchronous Rattus primitive.+isPrim :: GetCtxt => Var -> Maybe Prim+isPrim v+ | Just p <- Map.lookup v (primAlias ?ctxt) = Just p+ | otherwise = do+ (name,mod) <- getNameModule v+ if isRattModule mod then Map.lookup name primMap+ else Nothing+++-- | Checks whether a given expression is in fact a Asynchronous Rattus primitive.+isPrimExpr :: GetCtxt => LHsExpr GhcTc -> Maybe (Prim,Var)+isPrimExpr (L _ e) = isPrimExpr' e where+ isPrimExpr' :: GetCtxt => HsExpr GhcTc -> Maybe (Prim,Var)+ isPrimExpr' (HsVar _ (L _ v)) = fmap (,v) (isPrim v)+#if __GLASGOW_HASKELL__ >= 906+ isPrimExpr' (HsAppType _ e _ _) = isPrimExpr e+#else+ isPrimExpr' (HsAppType _ e _) = isPrimExpr e+#endif++ isPrimExpr' (XExpr (WrapExpr (HsWrap _ e))) = isPrimExpr' e+ isPrimExpr' (XExpr (ExpansionExpr (HsExpanded _ e))) = isPrimExpr' e+ isPrimExpr' (HsPragE _ _ e) = isPrimExpr e+#if __GLASGOW_HASKELL__ < 904+ isPrimExpr' (HsTick _ _ e) = isPrimExpr e+ isPrimExpr' (HsBinTick _ _ _ e) = isPrimExpr e+ isPrimExpr' (HsPar _ e) = isPrimExpr e+#else+ isPrimExpr' (XExpr (HsTick _ e)) = isPrimExpr e+ isPrimExpr' (XExpr (HsBinTick _ _ e)) = isPrimExpr e+ isPrimExpr' (HsPar _ _ e _) = isPrimExpr e+#endif++ isPrimExpr' _ = Nothing+++-- | This type class provides default implementations for 'check' and+-- 'checkBind' for Haskell syntax that is not supported. These default+-- implementations simply print an error message.+class NotSupported a where+ notSupported :: GetCtxt => SDoc -> CheckM a++instance NotSupported Bool where+ notSupported doc = printMessageCheck SevError ("Asynchronous Rattus does not support " <> doc)++instance NotSupported (Bool,Set Var) where+ notSupported doc = (,Set.empty) <$> notSupported doc+++-- | Add variables to the current context.+addVars :: Set Var -> Ctxt -> Ctxt+addVars vs c = c{current = vs `Set.union` current c }++-- | Print a message with the current location.+printMessage' :: GetCtxt => Severity -> SDoc -> CheckM ()+printMessage' sev doc =+ liftIO (modifyIORef (errorMsgs ?ctxt) ((sev ,srcLoc ?ctxt, doc) :))++-- | Print a message with the current location. Returns 'False', if+-- the severity is 'SevError' and otherwise 'True.+printMessageCheck :: GetCtxt => Severity -> SDoc -> CheckM Bool+printMessageCheck sev doc = printMessage' sev doc >>+ case sev of+ SevError -> return False+ _ -> return True
+ src/WidgetRattus/Plugin/SingleTick.hs view
@@ -0,0 +1,226 @@+-- | This module implements the translation from the multi-tick+-- calculus to the single tick calculus.++{-# LANGUAGE CPP #-}++module WidgetRattus.Plugin.SingleTick+ (toSingleTick) where++#if __GLASGOW_HASKELL__ >= 900+import GHC.Plugins+#else+import GhcPlugins+#endif++ +import WidgetRattus.Plugin.Utils+import Prelude hiding ((<>))+import Control.Monad.Trans.Writer.Strict+import Control.Monad.Trans.Class+import Data.List++-- | Transform the given expression from the multi-tick calculus into+-- the single tick calculus form.+toSingleTick :: CoreExpr -> CoreM CoreExpr+toSingleTick (Let (Rec bs) e) = do+ e' <- toSingleTick e+ bs' <- mapM (mapM toSingleTick) bs+ return (Let (Rec bs') e')+toSingleTick (Let (NonRec b e1) e2) = do+ e1' <- toSingleTick e1+ e2' <- toSingleTick e2+ return (Let (NonRec b e1') e2')+toSingleTick (Case e b ty alts) = do+ e' <- toSingleTick e+ alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (toSingleTick f)) . getAlt) alts+ return (Case e' b ty alts')+toSingleTick (Cast e c) = do+ e' <- toSingleTick e+ return (Cast e' c)+toSingleTick (Tick t e) = do+ e' <- toSingleTick e+ return (Tick t e')+toSingleTick (Lam x e) = do+ (e', advs) <- runWriterT (extractAdv' e)+ advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,a,b')) (toSingleTick b)) advs+ return (foldLets' advs' (Lam x e'))+toSingleTick (App e1 e2)+ | isDelayApp e1 = do+ (e2', advs) <- runWriterT (extractAdv e2)+ advs' <- mapM (mapM toSingleTick) advs+ return (foldLets advs' (App e1 e2'))+ | otherwise = do+ e1' <- toSingleTick e1+ e2' <- toSingleTick e2+ return (App e1' e2')++toSingleTick e@Type{} = return e+toSingleTick e@Var{} = return e+toSingleTick e@Lit{} = return e+toSingleTick e@Coercion{} = return e++foldLets :: [(Id,CoreExpr)] -> CoreExpr -> CoreExpr+foldLets ls e = foldl' (\e' (x,b) -> Let (NonRec x b) e') e ls++foldLets' :: [(Id,CoreExpr,CoreExpr)] -> CoreExpr -> CoreExpr+foldLets' ls e = foldl' (\e' (x,a,b) -> Let (NonRec x (App a b)) e') e ls++extractAdvApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractAdvApp e1 e2+ | isVar e2 = return (App e1 e2)+ | otherwise = do+ x <- lift (mkSysLocalFromExpr (fsLit "adv") e2)+ tell [(x,e2)]+ return (App e1 (Var x))++-- removes casts and ticks from a tree+filterTree :: CoreExpr -> CoreExpr+filterTree (Cast e _) = filterTree e+filterTree (Tick _ e) = filterTree e+filterTree e = e+++extractSelectApp :: CoreExpr -> CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractSelectApp e1 e2+ | isVar e' && isVar e2 = return (App e1 e2)+ | isVar e2 = do+ x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')+ tell [(x, e')]+ return (App (App e (Var x)) e2)+ | isVar e' = do+ x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)+ tell [(x, e2)]+ return (App e1 (Var x))+ | otherwise = do+ x <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e')+ y <- lift (mkSysLocalFromExpr (fsLit "selectFreshVar") e2)+ tell [(x, e')]+ tell [(y, e2)]+ return (App (App e (Var x)) (Var y))+ where (App e e') = filterTree e1+++-- This is used to pull adv out of delayed terms. The writer monad+-- returns mappings from fresh variables to terms that occur as+-- argument of adv.+-- +-- That is, occurrences of @adv t@ are replaced with @adv x@ (for some+-- fresh variable @x@) and the pair @(x,t)@ is returned in the+-- writer monad.+extractAdv :: CoreExpr -> WriterT [(Id,CoreExpr)] CoreM CoreExpr+extractAdv (App expr@(App e _) e2) | isSelectApp e = extractSelectApp expr e2+extractAdv e@(App e1 e2)+ | isAdvApp e1 = extractAdvApp e1 e2+ | isSelectApp e1 = extractSelectApp e1 e2+ | isDelayApp e1 = do+ (e2', advs) <- lift $ runWriterT (extractAdv e2)+ advs' <- mapM (mapM extractAdv) advs+ return (foldLets advs' (App e1 e2'))+ | isBoxApp e1 = lift $ toSingleTick e+ | otherwise = do+ e1' <- extractAdv e1+ e2' <- extractAdv e2+ return (App e1' e2')+extractAdv (Lam x e) = do+ (e', advs) <- lift $ runWriterT (extractAdv' e)+ advs' <- mapM (\ (x,a,b) -> fmap (\b' -> (x,b')) (extractAdvApp a b)) advs+ return (foldLets advs' (Lam x e'))+extractAdv (Case e b ty alts) = do+ e' <- extractAdv e+ alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv f)) . getAlt) alts+ return (Case e' b ty alts')+extractAdv (Cast e c) = do+ e' <- extractAdv e+ return (Cast e' c)+extractAdv (Tick t e) = do+ e' <- extractAdv e+ return (Tick t e')+extractAdv e@(Let Rec{} _) = lift $ toSingleTick e+extractAdv (Let (NonRec b e1) e2) = do+ e1' <- extractAdv e1+ e2' <- extractAdv e2+ return (Let (NonRec b e1') e2')+extractAdv e@Type{} = return e+extractAdv e@Var{} = return e+extractAdv e@Lit{} = return e+extractAdv e@Coercion{} = return e++-- This is used to pull adv out of lambdas. The writer monad returns+-- mappings from fresh variables to occurrences of adv and the term it+-- is applied to.+-- +-- That is occurrences of @adv t@ are replaced with a fresh variable+-- @x@ and the triple @(x,adv,t)@ is returned in the writer monad.+-- For select a b, the triple @(x, select a, b) is returned in the writer monad.+extractAdv' :: CoreExpr -> WriterT [(Id,CoreExpr,CoreExpr)] CoreM CoreExpr+extractAdv' e@(App e1 e2)+ | isAdvApp e1 = do+ x <- lift (mkSysLocalFromExpr (fsLit "adv") e)+ tell [(x,e1,e2)]+ return (Var x)+ | isSelectApp e1 = do+ x <- lift (mkSysLocalFromExpr (fsLit "select") e)+ tell [(x,e1,e2)]+ return (Var x)+ | isDelayApp e1 = do+ (e2', advs) <- lift $ runWriterT (extractAdv e2)+ advs' <- mapM (mapM extractAdv') advs+ return (foldLets advs' (App e1 e2'))+ | isBoxApp e1 = lift $ toSingleTick e+ | otherwise = do+ e1' <- extractAdv' e1+ e2' <- extractAdv' e2+ return (App e1' e2')+extractAdv' (Lam x e) = do+ e' <- extractAdv' e+ return (Lam x e')+extractAdv' (Case e b ty alts) = do+ e' <- extractAdv' e+ alts' <- mapM ((\ (c,bs,f) -> fmap (\ x -> mkAlt c bs x) (extractAdv' f)) . getAlt) alts+ return (Case e' b ty alts')+extractAdv' (Cast e c) = do+ e' <- extractAdv' e+ return (Cast e' c)+extractAdv' (Tick t e) = do+ e' <- extractAdv' e+ return (Tick t e')+extractAdv' e@(Let Rec{} _) = lift $ toSingleTick e+extractAdv' (Let (NonRec b e1) e2) = do+ e1' <- extractAdv' e1+ e2' <- extractAdv' e2+ return (Let (NonRec b e1') e2')+extractAdv' e@Type{} = return e+extractAdv' e@Var{} = return e+extractAdv' e@Lit{} = return e+extractAdv' e@Coercion{} = return e++++isDelayApp :: CoreExpr -> Bool+isDelayApp = isPrimApp (== "delay")++isBoxApp :: CoreExpr -> Bool+isBoxApp = isPrimApp (\occ -> occ == "Box" || occ == "box")++isAdvApp :: CoreExpr -> Bool+isAdvApp = isPrimApp (== "adv")++isSelectApp :: CoreExpr -> Bool+isSelectApp = isPrimApp (== "select")++isPrimApp :: (String -> Bool) -> CoreExpr -> Bool+isPrimApp p (App e e')+ | isType e' || not (tcIsLiftedTypeKind(typeKind (exprType e'))) = isPrimApp p e+ | otherwise = False+isPrimApp p (Cast e _) = isPrimApp p e+isPrimApp p (Tick _ e) = isPrimApp p e+isPrimApp p (Var v) = isPrimVar p v+isPrimApp _ _ = False++isPrimVar :: (String -> Bool) -> Var -> Bool+isPrimVar p v = maybe False id $ do+ let name = varName v+ mod <- nameModule_maybe name+ let occ = getOccString name+ return (p occ+ && moduleNameString (moduleName mod) == "WidgetRattus.InternalPrimitives")
+ src/WidgetRattus/Plugin/StableSolver.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+++-- | This module implements a constraint solver plugin for the+-- 'Stable' type class.++module WidgetRattus.Plugin.StableSolver (tcStable) where++import WidgetRattus.Plugin.Utils+ ( getNameModule, isRattModule, isStable )++import Prelude hiding ((<>))++import GHC.Plugins+ (Type, Var, CommandLineOption,tyConSingleDataCon,+ mkCoreConApps,getTyVar_maybe)+import GHC.Core+import GHC.Tc.Types.Evidence+import GHC.Core.Class+import GHC.Tc.Types+import GHC.Tc.Types.Constraint++import Data.Set (Set)+import qualified Data.Set as Set+#if __GLASGOW_HASKELL__ >= 904+import GHC.Types.Unique.FM+#endif++++-- | Constraint solver plugin for the 'Stable' type class.+tcStable :: [CommandLineOption] -> Maybe TcPlugin+tcStable _ = Just $ TcPlugin+ { tcPluginInit = return ()+ , tcPluginSolve = \ () -> stableSolver+ , tcPluginStop = \ () -> return ()+#if __GLASGOW_HASKELL__ >= 904+ , tcPluginRewrite = \ () -> emptyUFM+#endif+ }+++wrap :: Class -> Type -> EvTerm+wrap cls ty = EvExpr appDc+ where+ tyCon = classTyCon cls+ dc = tyConSingleDataCon tyCon+ appDc = mkCoreConApps dc [Type ty]++solveStable :: Set Var -> (Type, (Ct,Class)) -> Maybe (EvTerm, Ct)+solveStable c (ty,(ct,cl))+ | isStable c ty = Just (wrap cl ty, ct)+ | otherwise = Nothing++#if __GLASGOW_HASKELL__ >= 904+stableSolver :: EvBindsVar -> [Ct] -> [Ct] -> TcPluginM TcPluginSolveResult+stableSolver _ given wanted = do+#else+stableSolver :: [Ct] -> [Ct] -> [Ct] -> TcPluginM TcPluginResult+stableSolver given _derived wanted = do+#endif++ let chSt = concatMap filterCt wanted+ let haveSt = Set.fromList $ concatMap (filterTypeVar . fst) $ concatMap filterCt given+ case mapM (solveStable haveSt) chSt of+ Just evs -> return $ TcPluginOk evs []+ Nothing -> return $ TcPluginOk [] []++ where+#if __GLASGOW_HASKELL__ >= 908+ filterCt ct@(CDictCan (DictCt {di_cls = cl, di_tys = [ty]}))+#else+ filterCt ct@(CDictCan {cc_class = cl, cc_tyargs = [ty]})+#endif+ = case getNameModule cl of+ Just (name,mod)+ | isRattModule mod && name == "Stable" -> [(ty,(ct,cl))]+ _ -> []+ filterCt _ = []+ filterTypeVar ty = case getTyVar_maybe ty of+ Just v -> [v]+ Nothing -> []
+ src/WidgetRattus/Plugin/Strictify.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP #-}+module WidgetRattus.Plugin.Strictify+ (checkStrictData, SCxt (..)) where+import Prelude hiding ((<>))+import Control.Monad+import WidgetRattus.Plugin.Utils++import GHC.Plugins+import GHC.Types.Tickish++data SCxt = SCxt {srcSpan :: SrcSpan}++-- | Checks whether the given expression uses non-strict data types+-- and issues a warning if it finds any such use.+checkStrictData :: SCxt -> CoreExpr -> CoreM ()+checkStrictData ss (Let (NonRec _ e1) e2) = + checkStrictData ss e1 >> checkStrictData ss e2+checkStrictData ss (Case e _ _ alts) = do+ checkStrictData ss e+ mapM_ ((\(_,_,e) -> checkStrictData ss e) . getAlt) alts+checkStrictData ss (Let (Rec es) e) = do+ mapM_ (\ (_,e) -> checkStrictData ss e) es+ checkStrictData ss e+checkStrictData ss (Lam _ e) = checkStrictData ss e+checkStrictData ss (Cast e _) = checkStrictData ss e+checkStrictData ss (Tick (SourceNote span _) e) = + checkStrictData (ss{srcSpan = fromRealSrcSpan span}) e+checkStrictData ss (App e1 e2)+ | ignoreArgument e1 = return ()+ | otherwise = do + when (not (isType e2) && tcIsLiftedTypeKind(typeKind (exprType e2))+ && not (isStrict (exprType e2)) && not (isDeepseqForce e2) && not (isLit e2))+ (printMessage SevWarning (srcSpan ss)+ (text "The use of lazy type " <> ppr (exprType e2) <> " may lead to memory leaks. Use Control.DeepSeq.force on lazy types."))+ checkStrictData ss e1+ checkStrictData ss e2+checkStrictData _ss _ = return ()++isLit :: CoreExpr -> Bool+isLit Lit{} = True+isLit (App (Var v) Lit{}) + | Just (name,mod) <- getNameModule v = mod == "GHC.CString" && name == "unpackCString#"+isLit _ = False+++ignoreArgument :: CoreExpr -> Bool+ignoreArgument (Var v) =+ case getNameModule v of+ Just (name, mod) -> + ((mod == "GHC.Exts" || mod == "GHC.IsList") && (name == "fromList" || name == "fromListN")) ||+ (mod == "Data.String" && name == "fromString") ||+ (mod == "GHC.Stack.Types" && name == "pushCallStack") ||+ (mod == "Data.Text.Internal" && name == "pack")+ _ -> False+ignoreArgument (App x _) = ignoreArgument x+ignoreArgument _ = False++isDeepseqForce :: CoreExpr -> Bool+isDeepseqForce (App (App (App (Var v) _) _) _) =+ case getNameModule v of+ Just (name, mod) -> mod == "Control.DeepSeq" && name == "force"+ _ -> False+isDeepseqForce _ = False
+ src/WidgetRattus/Plugin/Transform.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE TupleSections #-}+module WidgetRattus.Plugin.Transform (+ transform+) where++import GHC.Core.Opt.Monad+import GHC.Plugins+import WidgetRattus.Plugin.PrimExpr+import WidgetRattus.Plugin.Utils+import Data.Maybe (fromJust)+import Prelude hiding ((<>))+import Data.Functor ((<&>))+import Control.Applicative ((<|>))+import Data.Tuple (swap)++data Ctx = Ctx {+ fresh :: Maybe Var+}++emptyCtx :: Ctx+emptyCtx = Ctx {+ fresh = Nothing+}++replaceVar :: Var -> Var -> Expr Var -> Expr Var+replaceVar match rep (Var v) = if v == match then Var rep else Var v+replaceVar match rep (App e e') = App (replaceVar match rep e) (replaceVar match rep e')+replaceVar match rep (Tick _ e) = replaceVar match rep e+replaceVar match rep (Lam v e) = Lam (if v == match then rep else v) (replaceVar match rep e)+replaceVar match rep (Let (NonRec b e') e) =+ Let (NonRec newB (replaceVar match rep e')) (replaceVar match rep e)+ where newB = if b == match then rep else b+replaceVar match rep (Cast e _) = replaceVar match rep e+replaceVar match rep (Case e b t alts) =+ Case newExpr newB t (map (\(Alt con binds expr) -> Alt con (map (\v -> if v == match then rep else v) binds) (replaceVar match rep expr)) alts)+ where newExpr = replaceVar match rep e+ newB = if b == match then rep else b+replaceVar _ _ e = e++transformPrim :: Ctx -> Expr Var -> CoreM (Expr Var, PrimInfo)+transformPrim ctx expr@(App e e') = case isPrimExpr expr of+ Just primInfo@(AdvApp f _) -> do+ varAdv' <- adv'Var+ let newE = replaceVar f varAdv' e+ return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)+ Just primInfo@(SelectApp f _ _) -> do+ varSelect' <- select'Var+ let newE = replaceVar f varSelect' e+ return (App (App newE e') (Var (fromJust $ fresh ctx)), primInfo)+ Just (DelayApp _ t) -> do+ bigDelayVar <- bigDelay+ inputValueV <- inputValueVar+ let inputValueType = mkTyConTy inputValueV + inpVar <- mkSysLocalM (fsLit "inpV") inputValueType inputValueType+ let ctx' = ctx {fresh = Just inpVar}+ (newExpr, maybePrimInfo) <- transform' ctx' e'+ let primInfo = fromJust maybePrimInfo+ let lambdaExpr = Lam inpVar newExpr+ clockCode <- constructClockExtractionCode primInfo+ return (App (App (App (Var bigDelayVar) (Type t)) clockCode) lambdaExpr, primInfo)+ Just primInfo -> do+ error $ showSDocUnsafe $ text "transformPrim: Cannot transform " <> ppr (prim primInfo)+ Nothing -> error "Cannot transform non-prim applications"+transformPrim _ _ = do+ error "Cannot transform anything else than prim applications"+++transform :: CoreExpr -> CoreM CoreExpr+transform expr = fst <$> transform' emptyCtx expr++transform' :: Ctx -> CoreExpr -> CoreM (CoreExpr, Maybe PrimInfo)+transform' ctx expr@(App e e') = case isPrimExpr expr of+ Just (BoxApp _) -> do+ (newExpr, primInfo) <- transform' ctx e'+ return (App e newExpr, primInfo)+ (Just _) -> do+ (newExpr, primInfo) <- transformPrim ctx expr+ return (newExpr, Just primInfo)+ Nothing -> do+ (newExpr, primInfo) <- transform' ctx e+ (newExpr', primInfo') <- transform' ctx e'+ return (App newExpr newExpr', primInfo <|> primInfo')+transform' ctx (Lam b rhs) = do+ (newExpr, primInfo) <- transform' ctx rhs+ return (Lam b newExpr, primInfo)+transform' ctx (Let (NonRec b rhs) e) = do+ (newRhs, primInfo) <- transform' ctx rhs+ (newExpr, primInfo') <- transform' ctx e+ return (Let (NonRec b newRhs) newExpr, primInfo <|> primInfo')+transform' ctx (Let (Rec binds) e) = do+ transformedBinds <- mapM (\(b, bindE) -> fmap (b,) (transform' ctx bindE)) binds+ (e', mPi) <- transform' ctx e+ let primInfos = map (\(_, (_, p)) -> p) transformedBinds+ let firstPrimInfo = foldl (<|>) mPi primInfos+ newBinds <- mapM (\(b, (e, _)) -> return (b, e)) transformedBinds+ return (Let (Rec newBinds) e', firstPrimInfo)+transform' ctx (Case e b t alts) = do+ -- The checking pass has ensured that there are not advances on different+ -- clocks. Thus we can just pick the first PrimInfo we find.+ (expr, primInfo) <- transform' ctx e++ -- For each alternative, transform it and save the maybePrimInfo-value+ transformed <- mapM (\(Alt con binds expr) -> transform' ctx expr <&> fmap (Alt con binds) . swap) alts++ -- Of all the primInfos we have, pick the first one. This is safe because+ -- the checking pass has ensured that the clocks of all primitives.+ let firstPrimInfo = foldl (\acc (p, _) -> acc <|> p) primInfo transformed+ let alts' = map snd transformed+ return (Case expr b t alts', firstPrimInfo)+transform' ctx (Cast e c) = do (e' , p) <- transform' ctx e; return (Cast e' c, p)+transform' ctx (Tick t e) = do (e' , p) <- transform' ctx e; return (Tick t e', p)+transform' _ e = return (e, Nothing)++constructClockExtractionCode :: PrimInfo -> CoreM CoreExpr+constructClockExtractionCode (AdvApp _ arg) = createClockCode arg+constructClockExtractionCode (SelectApp _ arg arg2) = clockUnion arg arg2+constructClockExtractionCode primInfo = error $ "Cannot construct clock for prim " ++ showSDocUnsafe (ppr (prim primInfo))+++createClockCode :: (Var, Type) -> CoreM CoreExpr+createClockCode (argV, argT) = do+ extractClock <- extractClockVar+ return $ App (App (Var extractClock) (Type argT)) (Var argV)++-- Generate code for union of two clocks.+-- clockUnion (aVar, aType) (bVar, bType) returns the AST for:+-- Set.union (extractClock aVar) (extractClock bVar)++clockUnion :: (Var,Type) -> (Var, Type) -> CoreM CoreExpr+clockUnion arg arg2 = do+ clock1Code <- createClockCode arg+ clock2Code <- createClockCode arg2+ unionVar' <- unionVar+ return $+ App+ (+ App+ (+ (Var unionVar')+ )+ clock1Code+ )+ clock2Code
+ src/WidgetRattus/Plugin/Utils.hs view
@@ -0,0 +1,384 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE CPP #-}++module WidgetRattus.Plugin.Utils (+ printMessage,+ Severity(..),+ isRattModule,+ adv'Var,+ select'Var,+ bigDelay,+ inputValueVar,+ extractClockVar,+ unionVar,+ isGhcModule,+ getNameModule,+ isStable,+ isStrict,+ isTemporal,+ userFunction,+ typeClassFunction,+ getVar,+ getMaybeVar,+ getModuleFS,+ isVar,+ isType,+ mkSysLocalFromVar,+ mkSysLocalFromExpr,+ fromRealSrcSpan,+ noLocationInfo,+ mkAlt,+ getAlt,+ splitForAllTys')+ where++#if __GLASGOW_HASKELL__ >= 908+import GHC.Types.Error (ResolvedDiagnosticReason (..))+#endif++#if __GLASGOW_HASKELL__ >= 906+import GHC.Builtin.Types.Prim+import GHC.Tc.Utils.TcType+#endif+#if __GLASGOW_HASKELL__ >= 904+import qualified GHC.Data.Strict as Strict+import Control.Concurrent.MVar (readMVar)+#else+import Data.IORef (readIORef)+#endif +import GHC.Utils.Logger+import GHC.Plugins+import GHC.Utils.Error hiding (errorMsg)+import GHC.Utils.Monad+++import GHC.Types.Name.Cache (NameCache(nsNames), lookupOrigNameCache, OrigNameCache)+import qualified GHC.Types.Name.Occurrence as Occurrence+import GHC.Types.TyThing++import Prelude hiding ((<>))+import Data.Set (Set)+import qualified Data.Set as Set+import Data.Char+import Data.Maybe+++getMaybeVar :: CoreExpr -> Maybe Var+getMaybeVar (App e e')+ | isType e' || not (tcIsLiftedTypeKind (typeKind (exprType e'))) = getMaybeVar e+ | otherwise = Nothing+getMaybeVar (Cast e _) = getMaybeVar e+getMaybeVar (Tick _ e) = getMaybeVar e+getMaybeVar (Var v) = Just v+getMaybeVar _ = Nothing++getVar :: CoreExpr -> Var+getVar = fromJust . getMaybeVar++isVar :: CoreExpr -> Bool+isVar = isJust . getMaybeVar++isType Type {} = True+isType (App e _) = isType e+isType (Cast e _) = isType e+isType (Tick _ e) = isType e+isType _ = False++#if __GLASGOW_HASKELL__ >= 906+isFunTyCon = isArrowTyCon+repSplitAppTys = splitAppTysNoView+#endif+ ++printMessage :: (HasDynFlags m, MonadIO m, HasLogger m) =>+ Severity -> SrcSpan -> SDoc -> m ()+printMessage sev loc doc = do+#if __GLASGOW_HASKELL__ >= 908+ logger <- getLogger+ liftIO $ putLogMsg logger (logFlags logger)+ (MCDiagnostic sev (if sev == SevError then ResolvedDiagnosticReason ErrorWithoutFlag else ResolvedDiagnosticReason WarningWithoutFlag) Nothing) loc doc+#elif __GLASGOW_HASKELL__ >= 906+ logger <- getLogger+ liftIO $ putLogMsg logger (logFlags logger)+ (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag) Nothing) loc doc+#elif __GLASGOW_HASKELL__ >= 904+ logger <- getLogger+ liftIO $ putLogMsg logger (logFlags logger)+ (MCDiagnostic sev (if sev == SevError then ErrorWithoutFlag else WarningWithoutFlag)) loc doc+#else+ dflags <- getDynFlags+ logger <- getLogger+ liftIO $ putLogMsg logger dflags NoReason sev loc doc+#endif++instance Ord FastString where+ compare = uniqCompareFS++{-+******************************************************+* Extracting variables *+******************************************************+-}+++origNameCache :: CoreM OrigNameCache+origNameCache = do+ hscEnv <- getHscEnv+#if __GLASGOW_HASKELL__ >= 904+ let nameCache = hsc_NC hscEnv+ liftIO $ readMVar (nsNames nameCache)+#else+ nameCache <- liftIO $ readIORef (hsc_NC hscEnv)+ return $ nsNames nameCache+#endif+++getNamedThingFromModuleAndOccName :: String -> OccName -> CoreM TyThing+getNamedThingFromModuleAndOccName moduleName occName = do+ origNameCache <- origNameCache+ case filter ((moduleName ==) . unpackFS . getModuleFS) (moduleEnvKeys origNameCache) of+ mod : _ -> lookupThing $ fromJust $ lookupOrigNameCache origNameCache mod occName+ _ -> error ( ("internal error: cannot find module " ++ moduleName ++ "; " ++ show (map (unpackFS . getModuleFS) $ moduleEnvKeys origNameCache)))++getVarFromModule :: String -> String -> CoreM Var+getVarFromModule moduleName = fmap tyThingId . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.varName++getTyConFromModule :: String -> String -> CoreM TyCon+getTyConFromModule moduleName = fmap tyThingTyCon . getNamedThingFromModuleAndOccName moduleName . mkOccName Occurrence.tcName++adv'Var :: CoreM Var+adv'Var = getVarFromModule "WidgetRattus.InternalPrimitives" "adv'"++select'Var :: CoreM Var+select'Var = getVarFromModule "WidgetRattus.InternalPrimitives" "select'"++bigDelay :: CoreM Var+bigDelay = getVarFromModule "WidgetRattus.InternalPrimitives" "Delay"++inputValueVar :: CoreM TyCon+inputValueVar = getTyConFromModule "WidgetRattus.InternalPrimitives" "InputValue"++extractClockVar :: CoreM Var+extractClockVar = getVarFromModule "WidgetRattus.InternalPrimitives" "extractClock"++unionVar :: CoreM Var+unionVar = getVarFromModule "WidgetRattus.InternalPrimitives" "clockUnion"++rattModules :: Set FastString+rattModules = Set.fromList ["WidgetRattus.InternalPrimitives","WidgetRattus.Channels"]++getModuleFS :: Module -> FastString+getModuleFS = moduleNameFS . moduleName++isRattModule :: FastString -> Bool+isRattModule = (`Set.member` rattModules)++ghcModules :: Set FastString+ghcModules = Set.fromList ["GHC.Types","GHC.Word","GHC.Int"]++isGhcModule :: FastString -> Bool+isGhcModule = (`Set.member` ghcModules)++getNameModule :: NamedThing a => a -> Maybe (FastString, FastString)+getNameModule v = do+ let name = getName v+ mod <- nameModule_maybe name+ return (getOccFS name,moduleNameFS (moduleName mod))+++-- | The set of stable built-in types.+ghcStableTypes :: Set FastString+ghcStableTypes = Set.fromList ["Word", "Word8", "Word16","Word32", "Word64","Int","Int8","Int16","Int32","Int64","Bool","Float","Double","Char", "IO"]++isGhcStableType :: FastString -> Bool+isGhcStableType = (`Set.member` ghcStableTypes)+++newtype TypeCmp = TC Type++instance Eq TypeCmp where+ (TC t1) == (TC t2) = eqType t1 t2++instance Ord TypeCmp where+ compare (TC t1) (TC t2) = nonDetCmpType t1 t2++isTemporal :: Type -> Bool+isTemporal t = isTemporalRec 0 Set.empty t+++isTemporalRec :: Int -> Set TypeCmp -> Type -> Bool+isTemporalRec d _ _ | d == 100 = False+isTemporalRec _ pr t | Set.member (TC t) pr = False+isTemporalRec d pr t = do+ let pr' = Set.insert (TC t) pr+ case splitTyConApp_maybe t of+ Nothing -> False+ Just (con,args) ->+ case getNameModule con of+ Nothing -> False+ Just (name,mod)+ -- If it's a Rattus type constructor check if it's a box+ | isRattModule mod && (name == "Box" || name == "O") -> True+ | isFunTyCon con -> or (map (isTemporalRec (d+1) pr') args)+ | isAlgTyCon con ->+ case algTyConRhs con of+ DataTyCon {data_cons = cons} -> or (map check cons)+ where check con = case dataConInstSig con args of+ (_, _,tys) -> or (map (isTemporalRec (d+1) pr') tys)+ _ -> or (map (isTemporalRec (d+1) pr') args)+ _ -> False+++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStable :: Set Var -> Type -> Bool+isStable c t = isStableRec c 0 Set.empty t++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStableRec :: Set Var -> Int -> Set TypeCmp -> Type -> Bool+-- To prevent infinite recursion (when checking recursive types) we+-- keep track of previously checked types. This, however, is not+-- enough for non-regular data types. Hence we also have a counter.+isStableRec _ d _ _ | d == 100 = True+isStableRec _ _ pr t | Set.member (TC t) pr = True+isStableRec c d pr t = do+ let pr' = Set.insert (TC t) pr+ case splitTyConApp_maybe t of+ Nothing -> case getTyVar_maybe t of+ Just v -> -- if it's a type variable, check the context+ v `Set.member` c+ Nothing -> False+ Just (con,args) ->+ case getNameModule con of+ Nothing -> False+ Just (name,mod)+ | mod == "GHC.Num.Integer" && name == "Integer" -> True+ | mod == "Data.Text.Internal" && name == "Text" -> True+ -- If it's a Rattus type constructor check if it's a box+ | isRattModule mod && (name == "Box" || name == "Chan") -> True+ -- If its a built-in type check the set of stable built-in types+ | isGhcModule mod -> isGhcStableType name+ {- deal with type synonyms (does not seem to be necessary (??))+ | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->+ isStableRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}+ | isAlgTyCon con ->+ case algTyConRhs con of+ DataTyCon {data_cons = cons, is_enum = enum}+ | enum -> True+ | all hasStrictArgs cons ->+ and (map check cons)+ | otherwise -> False+ where check con = case dataConInstSig con args of+ (_, _,tys) -> and (map (isStableRec c (d+1) pr') tys)+ TupleTyCon {} -> null args+ _ -> False+ _ -> False++++isStrict :: Type -> Bool+isStrict t = isStrictRec 0 Set.empty t++splitForAllTys' :: Type -> ([TyCoVar], Type)+splitForAllTys' = splitForAllTyCoVars++-- | Check whether the given type is stable. This check may use+-- 'Stable' constraints from the context.++isStrictRec :: Int -> Set TypeCmp -> Type -> Bool+-- To prevent infinite recursion (when checking recursive types) we+-- keep track of previously checked types. This, however, is not+-- enough for non-regular data types. Hence we also have a counter.+isStrictRec d _ _ | d == 100 = True+isStrictRec _ pr t | Set.member (TC t) pr = True+isStrictRec d pr t = do+ let pr' = Set.insert (TC t) pr+ let (_,t') = splitForAllTys' t+ let (c, tys) = repSplitAppTys t'+ if isJust (getTyVar_maybe c) then all (isStrictRec (d+1) pr') tys+ else case splitTyConApp_maybe t' of+ Nothing -> isJust (getTyVar_maybe t)+ Just (con,args) ->+ case getNameModule con of+ Nothing -> False+ Just (name,mod)+ | (mod == "GHC.Internal.IsList" || mod == "GHC.IsList" || mod == "GHC.Exts") && name == "Item" -> all (isStrictRec (d+1) pr') args+ | mod == "GHC.Num.Integer" && name == "Integer" -> True+ | mod == "Data.Text.Internal" && name == "Text" -> True+ | mod == "GHC.IORef" && name == "IORef" -> True+ | mod == "GHC.MVar" && name == "MVar" -> True+ -- If it's a Rattus type constructor check if it's a box+ | isRattModule mod && (name == "Box" || name == "Chan" || name == "O" || name == "Output") -> True+ -- If its a built-in type check the set of stable built-in types+ | isGhcModule mod -> isGhcStableType name+ {- deal with type synonyms (does not seem to be necessary (??))+ | Just (subst,ty,[]) <- expandSynTyCon_maybe con args ->+ isStrictRec c (d+1) pr' (substTy (extendTvSubstList emptySubst subst) ty) -}+ | isFunTyCon con -> True+ | isAlgTyCon con ->+ case algTyConRhs con of+ DataTyCon {data_cons = cons, is_enum = enum}+ | enum -> True+ | all hasStrictArgs cons ->+ all check cons+ | otherwise -> False+ where check con = case dataConInstSig con args of+ (_, _,tys) -> all (isStrictRec (d+1) pr') tys+ TupleTyCon {} -> null args+ NewTyCon {nt_rhs = ty} -> isStrictRec (d+1) pr' ty+ _ -> False+ | otherwise -> False++++++hasStrictArgs :: DataCon -> Bool+hasStrictArgs con = all isBanged (dataConImplBangs con)++userFunction :: Var -> Bool+userFunction v+ | typeClassFunction v = True+ | otherwise = + case getOccString (getName v) of+ (c : _)+ | isUpper c || c == '$' || c == ':' -> False+ | otherwise -> True+ _ -> False++typeClassFunction :: Var -> Bool+typeClassFunction v =+ case getOccString (getName v) of+ ('$' : 'c' : _) -> True+ ('$' : 'f' : _) -> True+ _ -> False++mkSysLocalFromVar :: MonadUnique m => FastString -> Var -> m Id+mkSysLocalFromVar lit v = mkSysLocalM lit (varMult v) (varType v)+ +mkSysLocalFromExpr :: MonadUnique m => FastString -> CoreExpr -> m Id+mkSysLocalFromExpr lit e = mkSysLocalM lit oneDataConTy (exprType e)+ + +fromRealSrcSpan :: RealSrcSpan -> SrcSpan+#if __GLASGOW_HASKELL__ >= 904+fromRealSrcSpan span = RealSrcSpan span Strict.Nothing+#else+fromRealSrcSpan span = RealSrcSpan span Nothing+#endif++instance Ord SrcSpan where+ compare (RealSrcSpan s _) (RealSrcSpan t _) = compare s t+ compare RealSrcSpan{} _ = LT+ compare _ _ = GT++noLocationInfo :: SrcSpan+noLocationInfo = UnhelpfulSpan UnhelpfulNoLocationInfo++mkAlt c args e = Alt c args e+getAlt (Alt c args e) = (c, args, e)
+ src/WidgetRattus/Primitives.hs view
@@ -0,0 +1,21 @@+-- | The language primitives of Async Rattus. Note that the typing+-- rules for 'delay', 'adv','select' and 'box' are more restrictive+-- than the Haskell types that are indicated. The stricter Async+-- Rattus typing rules for these primitives are given below.++{-# LANGUAGE TypeOperators #-}+module WidgetRattus.Primitives+ (O+ ,Box+ ,Select(..)+ ,delay+ ,adv+ ,promote+ ,box+ ,unbox+ ,select+ ,never+ ,Stable+ ,Continuous+ ) where+import WidgetRattus.InternalPrimitives
+ src/WidgetRattus/Signal.hs view
@@ -0,0 +1,492 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedLists #-}+++-- | Programming with signals.++module WidgetRattus.Signal+ ( map+ , mapAwait+ , switch+ , switchS+ , switchR+ , trigger+ , triggerAwait+ , triggerM+ , triggerAwaitM+ , buffer+ , bufferAwait+ , switchAwait+ , interleave+ , mapInterleave+ , interleaveAll+ , mkSig+ , mkBoxSig+ , current+ , future+ , const+ , jump+ , jumping+ , stop+ , scan+ , scanC+ , scanAwait+ , scanAwaitC+ , scanMap+ , Sig(..)+ , zipWith+ , zipWith3+ , zip+ , cond+ , update+ , integral+ , derivative+ )++where++import WidgetRattus+import Prelude hiding (map, const, zipWith, zipWith3, zip, filter)+import Data.VectorSpace+import Data.Ratio ((%))+-- TODO: InternalPrimitives is only used to implment instance of+-- Continuous. Replace this manual instance declaration with Template+-- Haskell.+import WidgetRattus.InternalPrimitives++infixr 5 :::++-- | @Sig a@ is a stream of values of type @a@.+data Sig a = !a ::: !(O (Sig a))++instance Producer (Sig a) a where+ getCurrent p = Just' (current p)+ getNext p cb = cb (future p)++newtype SigMaybe a = SigMaybe (Sig (Maybe' a))++instance Producer (SigMaybe a) a where+ getCurrent (SigMaybe p) = current p+ getNext (SigMaybe p) cb = cb (delay (SigMaybe (adv (future p))))++-- | Get the current value of a signal.+current :: Sig a -> a+current (x ::: _) = x+++-- | Get the future the signal.+future :: Sig a -> O (Sig a)+future (_ ::: xs) = xs++-- | Apply a function to the value of a signal.+map :: Box (a -> b) -> Sig a -> Sig b+map f (x ::: xs) = unbox f x ::: delay (map f (adv xs))++-- | A version of @map@ for delayed signals.+mapAwait :: Box (a -> b) -> O (Sig a) -> O (Sig b)+mapAwait f d = delay (map f (adv d))++-- | Turns a boxed delayed computation into a delayed signal.+mkSig :: Box (O a) -> O (Sig a)+mkSig b = delay (adv (unbox b) ::: mkSig b)++-- | Variant of 'mkSig' that returns a boxed delayed signal+mkBoxSig :: Box (O a) -> Box (O (Sig a))+mkBoxSig b = box (mkSig b)+++-- | Construct a constant signal that never updates.+const :: a -> Sig a+const x = x ::: never++-- | Similar to Haskell's 'scanl'.+--+-- > scan (box f) x (v1 ::: v2 ::: v3 ::: ... ) == (x `f` v1) ::: ((x `f` v1) `f` v2) ::: ...+--+-- Note: Unlike 'scanl', 'scan' starts with @x `f` v1@, not @x@.++scan :: (Stable b) => Box(b -> a -> b) -> b -> Sig a -> Sig b+scan f acc (a ::: as) = acc' ::: delay (scan f acc' (adv as))+ where acc' = unbox f acc a++-- | A variant of 'scan' that works with the 'C' monad.++scanC :: (Stable b) => Box(b -> a -> C b) -> b -> Sig a -> C (Sig b)+scanC f acc (a ::: as) = do+ acc' <- unbox f acc a+ fut <- delayC $ delay (scanC f acc' (adv as))+ return (acc' ::: fut)+ where + +-- | Like 'scan', but uses a delayed signal.+scanAwait :: (Stable b) => Box (b -> a -> b) -> b -> O (Sig a) -> Sig b+scanAwait f acc as = acc ::: delay (scan f acc (adv as))++-- | A variant of 'scanAwait' that works with the 'C' monad.++scanAwaitC :: (Stable b) => Box (b -> a -> C b) -> b -> O (Sig a) -> C (Sig b)+scanAwaitC f acc as = do + fut <- delayC $ delay (scanC f acc (adv as))+ return (acc ::: fut)++-- | 'scanMap' is a composition of 'map' and 'scan':+--+-- > scanMap f g x === map g . scan f x+scanMap :: (Stable b) => Box (b -> a -> b) -> Box (b -> c) -> b -> Sig a -> Sig c+scanMap f p acc (a ::: as) = unbox p acc' ::: delay (scanMap f p acc' (adv as))+ where acc' = unbox f acc a++-- | @jump (box f) xs@ first behaves like @xs@, but as soon as @f x =+-- Just xs'@ for a (current or future) value @x@ of @xs@, it behaves+-- like @xs'@.++jump :: Box (a -> Maybe' (Sig a)) -> Sig a -> Sig a+jump f (x ::: xs) = case unbox f x of+ Just' xs' -> xs'+ Nothing' -> x ::: delay (jump f (adv xs))+++-- | Similar to 'jump', but it can jump repeatedly. That is, @jumping+-- (box f) xs@ first behaves like @xs@, but every time @f x = Just+-- xs'@ for a (current or future) value @x@ of @jumping (box f) xs@,+-- it behaves like @xs'@.++jumping :: Box (a -> Maybe' (Sig a)) -> Sig a -> Sig a+jumping f (x ::: xs) = case unbox f x of+ Just' (x' ::: xs') -> x' ::: delay (jumping f (adv xs'))+ Nothing' -> x ::: delay (jumping f (adv xs))++-- | Stops as soon as the the predicate becomes true for the current+-- value. That is, @stop (box p) xs@ first behaves as @xs@, but as+-- soon as @f x = True@ for some (current or future) value @x@ of+-- @xs@, then it behaves as @const x@.+stop :: Box (a -> Bool) -> Sig a -> Sig a+stop p = jump (box (\ x -> if unbox p x then Just' (const x) else Nothing'))++-- | This function allows to switch from one signal to another one+-- dynamically. The signal defined by @switch xs ys@ first behaves+-- like @xs@, but as soon as @ys@ produces a new value, @switch xs ys@+-- behaves like @ys@.+--+-- Example:+--+-- > xs: 1 2 3 4 5 6 7 8 9+-- > ys: 1 2 3 4 5 6+-- >+-- > switch xs ys: 1 2 3 1 2 4 3 4 5 6+switch :: Sig a -> O (Sig a) -> Sig a+switch (x ::: xs) d = x ::: delay (case select xs d of+ Fst xs' d' -> switch xs' d'+ Snd _ d' -> d'+ Both _ d' -> d')++-- | This function is similar to 'switch', but the (future) second+-- signal may depend on the last value of the first signal.+switchS :: Stable a => Sig a -> O (a -> Sig a) -> Sig a+switchS (x ::: xs) d = x ::: delay (case select xs d of+ Fst xs' d' -> switchS xs' d'+ Snd _ f -> f x+ Both _ f -> f x)++-- | This function is similar to 'switch' but works on delayed signals+-- instead of signals.+switchAwait :: O (Sig a) -> O (Sig a) -> O (Sig a)+switchAwait xs ys = delay (case select xs ys of+ Fst xs' d' -> switch xs' d'+ Snd _ d' -> d'+ Both _ d' -> d')++-- | Variant of 'switchS' that repeatedly switches. The output signal+-- @switch xs ys@ first behaves like @xs@, but whenever @ys@ produces+-- a value @f@, the signal switches to @f v@ where @v@ is the previous+-- value of the output signal. +--+-- 'switchS' can be considered a special case of 'switchR' that only+-- makes a single switch. That is we have the following:+--+-- > switchS xs ys = switchR (delay (const (adv xs))) ys+switchR :: Stable a => Sig a -> O (Sig (a -> Sig a)) -> Sig a+switchR sig steps = switchS sig+ (delay (let step ::: steps' = adv steps in \ x -> switchR (step x) steps'))++-- | This function interleaves two signals producing a new value @v@+-- whenever either input stream produces a new value @v@. In case the+-- input signals produce a new value simultaneously, the function+-- argument is used break ties, i.e. to compute the new output value based+-- on the two new input values+--+-- Example:+--+-- > xs: 1 3 5 3 1 3+-- > ys: 0 2 4+-- >+-- > interleave (box (+)) xs ys: 1 3 2 5 7 1 3+interleave :: Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)+interleave f xs ys = delay (case select xs ys of+ Fst (x ::: xs') ys' -> x ::: interleave f xs' ys'+ Snd xs' (y ::: ys') -> y ::: interleave f xs' ys'+ Both (x ::: xs') (y ::: ys') -> unbox f x y ::: interleave f xs' ys')+++-- | This is the composition of 'mapAwait' and 'interleave'. That is,+-- +-- > mapInterleave f g xs ys = mapAwait f (interleave xs ys)+mapInterleave :: Box (a -> a) -> Box (a -> a -> a) -> O (Sig a) -> O (Sig a) -> O (Sig a)+mapInterleave g f xs ys = delay (case select xs ys of+ Fst (x ::: xs') ys' -> unbox g x ::: mapInterleave g f xs' ys'+ Snd xs' (y ::: ys') -> unbox g y ::: mapInterleave g f xs' ys'+ Both (x ::: xs') (y ::: ys') -> unbox g (unbox f x y) ::: mapInterleave g f xs' ys')+++{-# ANN interleaveAll AllowRecursion #-}+interleaveAll :: Box (a -> a -> a) -> List (O (Sig a)) -> O (Sig a)+interleaveAll _ Nil = error "interleaveAll: List must be nonempty"+interleaveAll _ [s] = s+interleaveAll f (x :! xs) = interleave f x (interleaveAll f xs)+++-- | Takes two signals and updates the first signal using the+-- functions produced by the second signal:+--+-- Law:+--+-- > (xs `update` fs) `update` gs = (xs `update` (interleave (box (.)) gs fs))+update :: (Stable a) => Sig a -> O (Sig (a -> a)) -> Sig a+update (x ::: xs) fs = x ::: delay + (case select xs fs of+ Fst xs' ys' -> update xs' ys'+ Snd xs' (f ::: fs') -> update (f x ::: xs') fs'+ Both (x' ::: xs') (f ::: fs') -> update (f x' ::: xs') fs')+++-- | This function is a variant of combines the values of two signals+-- using the function argument. @zipWith f xs ys@ produces a new value+-- @unbox f x y@ whenever @xs@ or @ys@ produce a new value, where @x@+-- and @y@ are the current values of @xs@ and @ys@, respectively.+--+-- Example:+--+-- > xs: 1 2 3 2+-- > ys: 1 0 5 2+-- >+-- > zipWith (box (+)) xs ys: 2 3 4 3 8 4++zipWith :: (Stable a, Stable b) => Box(a -> b -> c) -> Sig a -> Sig b -> Sig c+zipWith f (a ::: as) (b ::: bs) = unbox f a b ::: delay (+ case select as bs of+ Fst as' lbs -> zipWith f as' (b ::: lbs)+ Snd las bs' -> zipWith f (a ::: las) bs'+ Both as' bs' -> zipWith f as' bs'+ )++-- | Variant of 'zipWith' with three signals.+zipWith3 :: forall a b c d. (Stable a, Stable b, Stable c) => Box(a -> b -> c -> d) -> Sig a -> Sig b -> Sig c -> Sig d+zipWith3 f as bs cs = zipWith (box (\f x -> unbox f x)) cds cs+ where cds :: Sig (Box (c -> d))+ cds = zipWith (box (\a b -> box (\ c -> unbox f a b c))) as bs++-- | If-then-else lifted to signals. @cond bs xs ys@ produces a stream+-- whose value is taken from @xs@ whenever @bs@ is true and from @ys@+-- otherwise.+cond :: Stable a => Sig Bool -> Sig a -> Sig a -> Sig a+cond = zipWith3 (box (\b x y -> if b then x else y))+++-- | This is a special case of 'zipWith' using the tupling+-- function. That is,+--+-- > zip = zipWith (box (:*))+zip :: (Stable a, Stable b) => Sig a -> Sig b -> Sig (a:*b)+zip = zipWith (box (:*))++-- | This function is a variant of 'trigger' that works on a delayed+-- input signal. To this end, 'triggerAwait' takes an additional+-- argument that is the initial value of output signal.+--+-- Example:+--+-- > xs: 1 0 5 2+-- > ys: 5 1 2 3 2+-- >+-- > triggerAwait (box (+)) 0 xy ys: 0 2 2 2 3 8 4++triggerAwait :: (Stable b, Stable c) => Box (a -> b -> c) -> c -> O (Sig a) -> Sig b -> Sig c+triggerAwait f c as (b ::: bs) = c :::+ delay (case select as bs of+ Fst (a' ::: as') bs' -> triggerAwait f (unbox f a' b) as' (b ::: bs')+ Snd as' bs' -> triggerAwait f c as' bs'+ Both (a' ::: as') (b' ::: bs') -> triggerAwait f (unbox f a' b') as' (b' ::: bs'))+++-- | This function is a variant of 'triggerAwait' that only produces a+-- value when the first signal ticks; otherwise it produces+-- @Nothing'@.+--+-- Example:+--+-- > xs: 1 0 5 2+-- > ys: 5 1 2 3 2+-- >+-- > triggerAwaitM (box plus) xy ys: 2 N N 3 8 4 where plus x y =+-- Just' (x+y)++triggerAwaitM :: Stable b => Box (a -> b -> Maybe' c) -> O (Sig a) -> Sig b -> O (Sig (Maybe' c))+triggerAwaitM f as (b ::: bs) = + delay (case select as bs of+ Fst (a' ::: as') bs' -> unbox f a' b ::: triggerAwaitM f as' (b ::: bs')+ Snd as' bs' -> Nothing' ::: triggerAwaitM f as' bs'+ Both (a' ::: as') (b' ::: bs') -> unbox f a' b' ::: triggerAwaitM f as' (b' ::: bs'))++-- | This function is a variant of 'zipWith'. Whereas @zipWith f xs+-- ys@ produces a new value whenever @xs@ or @ys@ produce a new value,+-- @trigger f xs ys@ only produces a new value when xs produces a new+-- value, otherwise it just repeats the previous value.+--+-- Example:+--+-- > xs: 1 0 5 2+-- > ys: 1 2 3 2+-- >+-- > zipWith (box (+)) xs ys: 2 3 4 3 8 4+-- > trigger (box (+)) xy ys: 2 2 2 3 8 4++trigger :: (Stable b, Stable c) => Box (a -> b -> c) -> Sig a -> Sig b -> Sig c+trigger f (a:::as) bs@(b ::: _) = triggerAwait f (unbox f a b) as bs++-- | This function is a variant of 'trigger' that only produces a+-- value when the first signal ticks; otherwise it produces+-- @Nothing'@.+--+-- Example:+--+-- > xs: 1 0 5 2+-- > ys: 1 2 3 2+-- >+-- > zipWith (box plus) xs ys: 2 3 4 3 8 4+-- > trigger (box plus) xy ys: 2 N N 3 8 4+-- where+-- > plus x y = Just' (x+y)++triggerM :: Stable b => Box (a -> b -> Maybe' c) -> Sig a -> Sig b -> Sig (Maybe' c)+triggerM f (a:::as) bs@(b ::: _) = unbox f a b ::: triggerAwaitM f as bs+++-- Buffer takes an initial value and a signal as input and returns a signal that+-- is always one tick behind the input signal.+buffer :: Stable a => a -> Sig a -> Sig a+buffer x (y ::: ys) = x ::: delay (buffer y (adv ys))++-- Like buffer but works for delayed signals+bufferAwait :: Stable a => a -> O (Sig a) -> O (Sig a)+bufferAwait x xs = delay (buffer x (adv xs))++-- | Sampling interval (in microseconds) for the 'integral' and+-- 'derivative' functions.++dt :: Int+dt = 20000++-- | @integral x xs@ computes the integral of the signal @xs@ with the+-- constant @x@. For example, if @xs@ is the velocity of an object,+-- the signal @integral 0 xs@ describes the distance travelled by that+-- object.+integral :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)+ => v -> Sig v -> Sig v+integral = int + where int cur (x ::: xs)+ | x == zeroVector = cur ::: delay (int cur (adv xs))+ | otherwise = cur ::: delay (+ case select xs (unbox (timer dt)) of+ Fst xs' _ -> int cur xs'+ Snd xs' _ -> int (dtf *^ (cur ^+^ x)) (x ::: xs')+ Both (x' ::: xs') _ -> int (dtf *^ (cur ^+^ x')) (x'::: xs'))+ -- sampling interval in seconds+ dtf :: a+ dtf = fromRational (fromIntegral dt % 1000000)+ +-- | Compute the derivative of a signal. For example, if @xs@ is the+-- velocity of an object, the signal @derivative xs@ describes the+-- acceleration travelled by that object.+derivative :: forall a v . (VectorSpace v a, Eq v, Fractional a, Stable v, Stable a)+ => Sig v -> Sig v+derivative xs = der zeroVector (current xs) xs where+ -- inverse sampling interval in seconds+ dtf :: a+ dtf = fromIntegral dt * 0.000001++ der :: v -> v -> Sig v -> Sig v+ der d last (x:::xs)+ | d == zeroVector = zeroVector ::: delay+ (let x' ::: xs' = adv xs+ in der ((x' ^-^ x) ^/ dtf) x (x' ::: xs'))+ | otherwise = d ::: delay (+ case select xs (unbox (timer dt)) of+ Fst xs' _ -> der d last xs'+ Snd xs' _ -> der ((x ^-^ last) ^/ dtf) x (x ::: xs')+ Both (x' ::: xs') _ -> der ((x' ^-^ last) ^/ dtf) x' (x' ::: xs'))+++instance Continuous a => Continuous (Sig a) where+ progressInternal inp (x ::: xs@(Delay cl _)) = + if inputInClock inp cl then (adv' xs inp)+ else progressInternal inp x ::: xs+ progressAndNext inp (x ::: xs@(Delay cl _)) = + if inputInClock inp cl then let n = adv' xs inp in (n, nextProgress n)+ else let (n , cl') = progressAndNext inp x in (n ::: xs , cl `clockUnion` cl')+ nextProgress (x ::: (Delay cl _)) = nextProgress x `clockUnion` cl++-- Prevent functions from being inlined too early for the rewrite+-- rules to fire.++{-# NOINLINE [1] map #-}+{-# NOINLINE [1] const #-}+{-# NOINLINE [1] scan #-}+{-# NOINLINE [1] scanMap #-}+{-# NOINLINE [1] zip #-}+{-# NOINLINE [1] update #-}+{-# NOINLINE [1] switch #-}+{-# NOINLINE [1] interleave #-}+{-# NOINLINE [1] mapAwait #-}+++{-# RULES++ "const/switch" forall x xs.+ switch (const x) xs = x ::: xs;++ "update/update" forall xs fs gs.+ update (update xs fs) gs = update xs (interleave (box (.)) gs fs) ;++ "const/map" forall (f :: Stable b => Box (a -> b)) x.+ map f (const x) = let x' = unbox f x in const x' ;++ "map/map" forall f g xs.+ map f (map g xs) = map (box (unbox f . unbox g)) xs ;++ "map/scan" forall f p acc as.+ map p (scan f acc as) = scanMap f p acc as ;++ "mapAwait/interleave" forall f g xs ys.+ mapAwait f (interleave g xs ys) = mapInterleave f g xs ys ;++ "zip/map" forall xs ys f.+ map f (zip xs ys) = let f' = unbox f in zipWith (box (\ x y -> f' (x :* y))) xs ys;++ "scan/scan" forall f g b c as.+ scan g c (scan f b as) =+ let f' = unbox f; g' = unbox g in+ scanMap (box (\ (b:*c) a -> let b' = f' b a in (b':* g' c b'))) (box snd') (b:*c) as ;++ "scan/scanMap" forall f g p b c as.+ scan g c (scanMap f p b as) =+ let f' = unbox f; g' = unbox g; p' = unbox p in+ scanMap (box (\ (b:*c) a -> let b' = f' (p' b) a in (b':* g' c b'))) (box snd') (b:*c) as ;++#-}+
+ src/WidgetRattus/Strict.hs view
@@ -0,0 +1,275 @@+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE TemplateHaskell #-}+++-- | This module contains strict versions of some standard data+-- structures.++++module WidgetRattus.Strict+ ( List(..),+ singleton,+ IsList(..),+ init',+ reverse',+ union',+ unionBy',+ nub',+ nubBy',+ filter',+ delete',+ deleteBy',+ (+++),+ listToMaybe',+ map',+ zip',+ zipWith',+ mapMaybe',+ concatMap',+ (:*)(..),+ Maybe'(..),+ maybe',+ fromMaybe',+ isJust',+ fst',+ snd',+ curry',+ uncurry',+ toText,+ readMaybe',+ splitOn'+ )where++import Prelude hiding (map)+import Data.VectorSpace+import WidgetRattus.Derive+import WidgetRattus.Plugin.Annotation+import GHC.Exts (IsList(..))+import Data.Text hiding (foldl, singleton)+import Text.Read (readMaybe)++infixr 2 :*+-- | Strict pair type.+data a :* b = !a :* !b++continuous ''(:*)++-- | First projection function.+fst' :: (a :* b) -> a+fst' (a:*_) = a++-- | Second projection function.+snd' :: (a :* b) -> b+snd' (_:*b) = b++curry' :: ((a :* b) -> c) -> a -> b -> c+curry' f x y = f (x :* y)++uncurry' :: (a -> b -> c) -> (a :* b) -> c+uncurry' f (x :* y) = f x y+++instance Functor ((:*) a) where+ fmap f (x:*y) = (x :* f y)+ +instance (Show a, Show b) => Show (a:*b) where+ show (a :* b) = "(" ++ show a ++ " :* " ++ show b ++ ")"++instance (Eq a, Eq b) => Eq (a :* b) where+ (x1 :* y1) == (x2 :* y2) = x1 == x2 && y1 == y2+++instance (VectorSpace v a, VectorSpace w a, Floating a, Eq a) => VectorSpace (v :* w) a where+ zeroVector = zeroVector :* zeroVector++ a *^ (x :* y) = (a *^ x) :* (a *^ y)++ (x :* y) ^/ a = (x ^/ a) :* (y ^/ a)++ negateVector (x :* y) = (negateVector x) :* (negateVector y)++ (x1 :* y1) ^+^ (x2 :* y2) = (x1 ^+^ x2) :* (y1 ^+^ y2)++ (x1 :* y1) ^-^ (x2 :* y2) = (x1 ^-^ x2) :* (y1 ^-^ y2)++ (x1 :* y1) `dot` (x2 :* y2) = (x1 `dot` x2) + (y1 `dot` y2)++infixr 8 :!++-- | Strict list type.+data List a = Nil | !a :! !(List a)++continuous ''List+++singleton :: a -> List a+singleton x = x :! Nil++instance Traversable List where+ traverse _ Nil = pure Nil+ traverse f (x :! xs) = (:!) <$> (f x) <*> (traverse f xs)++instance IsList (List a) where+ type Item (List a) = a++ fromList [] = Nil+ fromList (x : xs) = x :! fromList xs++ toList Nil = []+ toList (x :! xs) = x : toList xs+++-- | Remove the last element from a list if there is one, otherwise+-- return 'Nil'.+init' :: List a -> List a+init' Nil = Nil+init' (_ :! Nil) = Nil+init' (x :! xs) = x :! init' xs++-- | Reverse a list.+reverse' :: List a -> List a+reverse' l = rev l Nil+ where+ rev Nil a = a+ rev (x:!xs) a = rev xs (x:!a)+ +-- | Returns @'Nothing''@ on an empty list or @'Just'' a@ where @a@ is the+-- first element of the list.+listToMaybe' :: List a -> Maybe' a+listToMaybe' Nil = Nothing'+listToMaybe' (x :! _) = Just' x++-- | Append two lists.+(+++) :: List a -> List a -> List a+(+++) Nil ys = ys+(+++) (x:!xs) ys = x :! xs +++ ys+++map' :: (a -> b) -> List a -> List b+map' _ Nil = Nil+map' f (x :! xs) = f x :! map' f xs++concatMap' :: (a -> List b) -> List a -> List b+concatMap' _ Nil = Nil+concatMap' f (x :! xs) = f x +++ concatMap' f xs++zip' :: List a -> List b -> List (a :* b)+zip' Nil _ = Nil+zip' _ Nil = Nil+zip' (x :! xs) (y :! ys) = (x :* y) :! zip' xs ys++zipWith' :: (a -> b -> c) -> List a -> List b -> List c+zipWith' _ Nil _ = Nil+zipWith' _ _ Nil = Nil+zipWith' f (x :! xs) (y :! ys) = f x y :! zipWith' f xs ys+++-- | A version of 'map' which can throw out elements. In particular,+-- the function argument returns something of type @'Maybe'' b@. If+-- this is 'Nothing'', no element is added on to the result list. If+-- it is @'Just'' b@, then @b@ is included in the result list.+mapMaybe' :: (a -> Maybe' b) -> List a -> List b+mapMaybe' _ Nil = Nil+mapMaybe' f (x:!xs) =+ let rs = mapMaybe' f xs in+ case f x of+ Nothing' -> rs+ Just' r -> r:!rs++isJust' :: Maybe' a -> Bool+isJust' (Just' _) = True+isJust' Nothing' = False++union' :: (Eq a) => List a -> List a -> List a+union' = unionBy' (==)++unionBy' :: (a -> a -> Bool) -> List a -> List a -> List a+unionBy' eq xs ys = xs +++ foldl (flip (deleteBy' eq)) (nubBy' eq ys) xs++delete' :: (Eq a) => a -> List a -> List a+delete' = deleteBy' (==)++deleteBy' :: (a -> a -> Bool) -> a -> List a -> List a+deleteBy' _ _ Nil = Nil+deleteBy' eq x (y :! ys) = if x `eq` y then ys else y :! deleteBy' eq x ys+++nub' :: (Eq a) => List a -> List a+nub' = nubBy' (==)++nubBy' :: (a -> a -> Bool) -> List a -> List a+nubBy' _ Nil = Nil+nubBy' eq (x:!xs) = x :! nubBy' eq (filter' (\ y -> not (eq x y)) xs)++filter' :: (a -> Bool) -> List a -> List a+filter' _ Nil = Nil+filter' pred (x :! xs)+ | pred x = x :! filter' pred xs+ | otherwise = filter' pred xs++instance Foldable List where+ + foldMap f = run where+ run Nil = mempty+ run (x :! xs) = f x <> run xs+ foldr f = run where+ run b Nil = b+ run b (a :! as) = (run $! (f a b)) as+ foldl f = run where+ run a Nil = a+ run a (b :! bs) = (run $! (f a b)) bs+ elem a = run where+ run Nil = False+ run (x :! xs)+ | a == x = True+ | otherwise = run xs+ + +instance Functor List where+ fmap = map'++instance Eq a => Eq (List a) where+ Nil == Nil = True+ Nil == _ = False+ _ == Nil = False+ (x :! xs) == (y :! ys) = if x == y then xs == ys else False++instance Show a => Show (List a) where+ show Nil = "Nil"+ show (x :! xs) = show x ++ " :! " ++ show xs++-- | Strict variant of 'Maybe'.+data Maybe' a = Just' !a | Nothing' deriving (Show, Eq, Ord)++continuous ''Maybe'++-- | takes a default value, a function, and a 'Maybe'' value. If the+-- 'Maybe'' value is 'Nothing'', the function returns the default+-- value. Otherwise, it applies the function to the value inside the+-- 'Just'' and returns the result.+maybe' :: b -> (a -> b) -> Maybe' a -> b+maybe' n _ Nothing' = n+maybe' _ f (Just' x) = f x++fromMaybe' :: a -> Maybe' a -> a+fromMaybe' _ (Just' x) = x+fromMaybe' d Nothing' = d+++{-# ANN toText AllowLazyData #-}+toText :: Show a => a -> Text+toText x = pack (show x)++{-# ANN readMaybe' AllowLazyData #-}+readMaybe' :: Read a => Text -> Maybe' a+readMaybe' x = case readMaybe (unpack x) of + Just x -> Just' x+ Nothing -> Nothing'+ +splitOn' :: Text -> Text -> List Text+splitOn' x y = fromList (splitOn x y)
+ src/WidgetRattus/Widgets.hs view
@@ -0,0 +1,217 @@+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE TypeOperators #-}+++module WidgetRattus.Widgets + ( Displayable (..)+ , IsWidget (..)+ , Widgets (..)+ , Widget+ , HStack+ , VStack+ , TextDropdown+ , tddCurr+ , tddEvent+ , tddList+ , Popup+ , popCurr+ , popEvent+ , popChild+ , Slider+ , sldCurr+ , sldEvent+ , sldMin+ , sldMax+ , Button+ , btnContent+ , btnClick+ , Label+ , labText+ , TextField+ , tfContent+ , tfInput+ , mkButton+ , mkTextField+ , addInputSigTF+ , mkLabel+ , mkHStack+ , mkConstHStack+ , mkVStack+ , mkConstVStack+ , mkTextDropdown+ , mkPopup+ , mkSlider+ , mkProgressBar+ , btnOnClick+ , btnOnClickSig+ , setInputSigTF+ , textFieldOnInput+ , textFieldOnInputSig+ , runApplication+ ) where++import WidgetRattus+import WidgetRattus.Widgets.InternalTypes+import WidgetRattus.Signal+import Data.Text+import WidgetRattus.InternalPrimitives+import System.IO.Unsafe+import Control.Concurrent hiding (Chan)+import Data.IntSet as IntSet+import Prelude hiding (const)++import qualified Monomer as M++-- The identity function.+instance Displayable Text where+ display x = x++-- Convert Int to Text via String.+instance Displayable Int where + display x = toText x+++-- Functions for constructing Async Rattus widgets. +mkButton :: (Displayable a) => Sig a -> C Button+mkButton t = do+ c <- chan+ return Button{btnContent = t, btnClick = c}++mkTextField :: Text -> C TextField+mkTextField txt = do+ c <- chan+ let sig = txt ::: mkSig (box (wait c))+ return TextField{tfContent = sig, tfInput = c}++mkLabel :: (Displayable a) => Sig a -> C Label+mkLabel t = do+ return Label{labText = t}++class Widgets ws where+ toWidgetList :: ws -> List Widget++instance {-# OVERLAPPABLE #-} IsWidget w => Widgets w where+ toWidgetList w = [ mkWidget w ]++instance {-# OVERLAPPING #-} (Widgets w, Widgets v) => Widgets (w :* v) where+ toWidgetList (w :* v) = toWidgetList w +++ toWidgetList v+++instance {-# OVERLAPPING #-} (Widgets w) => Widgets (List w) where+ toWidgetList w = concatMap' toWidgetList w+++mkHStack :: IsWidget a => Sig(List a) -> C HStack+mkHStack wl = do+ return (HStack wl)+ +mkConstHStack :: Widgets ws => ws -> C HStack+mkConstHStack w = mkHStack (const (toWidgetList w))++mkVStack :: IsWidget a => Sig(List a) -> C VStack+mkVStack wl = do+ return (VStack wl)++mkConstVStack :: Widgets ws => ws -> C VStack+mkConstVStack w = mkVStack (const (toWidgetList w))++mkTextDropdown :: Sig (List Text) -> Text -> C TextDropdown+mkTextDropdown opts init = do+ c <- chan+ let curr = init ::: mkSig (box (wait c))+ return TextDropdown{tddCurr = curr, tddEvent = c, tddList = opts}++mkPopup :: Sig Bool -> Sig Widget -> C Popup +mkPopup b w = do+ c <- chan+ let sig = current b ::: interleave (box (\x _ -> x)) (future b) (mkSig (box (wait c)))+ return Popup{popCurr = sig, popEvent = c, popChild = w}++mkSlider :: Int -> Sig Int -> Sig Int -> C Slider+mkSlider start min max = do+ c <- chan+ let curr = start ::: mkSig (box (wait c))+ return Slider{sldCurr = curr, sldEvent = c, sldMin = min, sldMax = max}++mkProgressBar :: Sig Int -> Sig Int -> Sig Int -> C Slider+mkProgressBar min max curr = do+ c <- chan+ let boundedCurrent = WidgetRattus.Signal.zipWith (box Prelude.min) curr max+ return Slider{sldCurr = boundedCurrent, sldEvent = c, sldMin = min, sldMax = max}+++-- Helper function that takes a Button and returns a boxed delayed computation.+-- The delayed computation is defined from the buttons input channel.+btnOnClick :: Button -> Box(O())+btnOnClick btn =+ let ch = btnClick btn+ in box (wait ch)++-- Function that constructs a delayed signal from a Button.+btnOnClickSig :: Button -> O (Sig ())+btnOnClickSig btn = mkSig (btnOnClick btn)++-- Creates a new textfield whose contents are determined by+-- the input signal.+-- Therefore user input will only be shown if the input signal+-- ticks in response to user input on the textfield.+-- Note: the input TF and output TF share an input channel+-- Hence if both are part of a GUI they will be written to simultaneously+setInputSigTF :: TextField -> Sig Text -> TextField+setInputSigTF tf sig = tf{tfContent = sig} ++-- Uses the input signal to create a new textfield+-- The returned textfield updates in response to the input signal+-- as well as the content signal of the original textfield.+-- Note: the input TF and output TF share an input channel+-- Hence if both are part of a GUI they will be written to simultaneously+addInputSigTF :: TextField -> O (Sig Text) -> TextField+addInputSigTF tf sig =+ let leaved = current (tfContent tf) ::: interleave (box (\x _ -> x)) (future (tfContent tf)) sig+ in tf{tfContent = leaved, tfInput = tfInput tf}++-- Helper function that takes a TextField and returns a boxed delayed computation.+-- The delayed computation is defined from the Textfields input channel.+textFieldOnInput :: TextField -> Box(O Text)+textFieldOnInput tf =+ let ch = tfInput tf+ in box (wait ch)++-- Function that constructs a delayed signal from a Textfield.+textFieldOnInputSig :: TextField -> O (Sig Text)+textFieldOnInputSig tf = mkSig (textFieldOnInput tf)+++-- Function which creates a timed event. Associated clock will be part of the AppModel.+mkTimerEvent :: Int -> (AppEvent -> IO ()) -> IO ()+mkTimerEvent n cb = (threadDelay n >> cb (AppEvent (Chan n) ())) >> return ()+++-- runApplication takes as input a widget and starts the GUI applicaiton+-- by calling Monomer's startApp function.+{-# ANN runApplication AllowLazyData #-}+runApplication :: IsWidget a => C a -> IO ()+runApplication (C w) = do+ w' <- w+ M.startApp (AppModel w' emptyClock) handler builder config+ where builder _ (AppModel w _) = mkWidgetNode w `M.styleBasic` [M.padding 3]+ handler _ _ (AppModel w cl) (AppEvent (Chan ch) d) =+ let inp = OneInput ch d in unsafePerformIO $ do+ progressPromoteStoreAtomic inp+ let (w' , cl') = progressAndNext inp w+ let activeTimers = if ch > 0 then IntSet.delete ch cl else cl+ let newTimers = IntSet.filter (> 0) cl' `IntSet.difference` activeTimers+ let timers = Prelude.map (M.Producer . mkTimerEvent) (IntSet.toList newTimers)+ return (M.Model (AppModel w' (newTimers `IntSet.union` activeTimers)) : M.Request M.RenderOnce : timers )+ config = [+ M.appWindowTitle "GUI Application",+ M.appTheme M.lightTheme,+ M.appFontDef "Regular" "./assets/fonts/Roboto-Regular.ttf",+ M.appInitEvent (AppEvent (Chan 1) ())+ ]
+ src/WidgetRattus/Widgets/InternalTypes.hs view
@@ -0,0 +1,122 @@++{-# OPTIONS -fplugin=WidgetRattus.Plugin #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE GADTs #-}+++module WidgetRattus.Widgets.InternalTypes where++import WidgetRattus+import WidgetRattus.InternalPrimitives+import WidgetRattus.Signal+import Data.Text++import qualified Monomer as M+{-# ANN module AllowLazyData #-}++-- The Displayable typeclass is used to define the display function.+-- The display function is used to convert a datatype to Text.+class Stable a => Displayable a where+ display :: a -> Text++-- The AppModel datatype used to contain the Widget passed to runApplication. +-- The associated clock is a set of timers. +-- Any timers created with mkTimerEvent will be added to the clock.+data AppModel where+ AppModel :: IsWidget a => !a -> !Clock -> AppModel++++instance (Eq AppModel) where+ _ == _ = False+++-- AppEvent data type used to convert channels into events.+data AppEvent where+ AppEvent :: !(Chan a) -> !a -> AppEvent++-- The IsWidget typeclass is used to define the mkWidgetNode function.+class Continuous a => IsWidget a where+ mkWidgetNode :: a -> M.WidgetNode AppModel AppEvent+ + mkWidget :: a -> Widget+ mkWidget w = Widget w (WidgetRattus.Signal.const True)+++ setEnabled :: a -> Sig Bool -> Widget+ setEnabled = Widget++-- Custom data types for widgets.+data Widget where+ Widget :: IsWidget a => !a -> !(Sig Bool) -> Widget++data HStack where + HStack :: IsWidget a => !(Sig (List a)) -> HStack++data VStack where + VStack :: IsWidget a => !(Sig (List a)) -> VStack++data TextDropdown = TextDropdown {tddCurr :: !(Sig Text), tddEvent :: !(Chan Text), tddList :: !(Sig (List Text))}++data Popup = Popup {popCurr :: !(Sig Bool), popEvent :: !(Chan Bool), popChild :: !(Sig Widget)}++data Slider = Slider {sldCurr :: !(Sig Int), sldEvent :: !(Chan Int), sldMin :: !(Sig Int), sldMax :: !(Sig Int)}++data Button where+ Button :: (Displayable a) => {btnContent :: !(Sig a) , btnClick :: !(Chan ())} -> Button+++data Label where+ Label :: (Displayable a) => {labText :: !(Sig a)} -> Label++data TextField = TextField {tfContent :: !(Sig Text), tfInput :: !(Chan Text)} ++-- Template Haskell code for generating instances of Continous.+continuous ''Button+continuous ''TextField+continuous ''Label+continuous ''Widget+continuous ''HStack+continuous ''VStack+continuous ''TextDropdown+continuous ''Popup+continuous ''Slider++-- isWidget Instance declerations for Widgets.+-- Here widgget data types are passed to Monomer constructors.+instance IsWidget Button where+ mkWidgetNode Button{btnContent = txt ::: _ , btnClick = click} =+ M.button (display txt) (AppEvent click ())++instance IsWidget TextField where+ mkWidgetNode TextField{tfContent = txt ::: _, tfInput = inp} = + M.textFieldV txt (AppEvent inp)++instance IsWidget Label where+ mkWidgetNode Label{labText = txt ::: _} = M.label (display txt)+++instance IsWidget HStack where+ mkWidgetNode (HStack ws) = M.hstack_ [ M.childSpacing_ 2] (reverse' $ fmap mkWidgetNode (current ws))++instance IsWidget VStack where+ mkWidgetNode (VStack ws) = M.vstack_ [ M.childSpacing_ 2] (reverse' $ fmap mkWidgetNode (current ws))++instance IsWidget TextDropdown where+ mkWidgetNode TextDropdown{tddList = opts ::: _, tddCurr = curr ::: _, tddEvent = ch}+ = M.textDropdownV curr (AppEvent ch) opts++instance IsWidget Popup where+ mkWidgetNode Popup{popCurr = curr ::: _, popEvent = ch, popChild = child}+ = M.popupV curr (AppEvent ch) (mkWidgetNode (current child))++instance IsWidget Slider where+ mkWidgetNode Slider{sldCurr = curr ::: _, sldEvent = ch, sldMin = min ::: _, sldMax = max ::: _}+ = M.hsliderV curr (AppEvent ch) min max++instance IsWidget Widget where+ mkWidgetNode (Widget w (e ::: _)) = M.nodeEnabled (mkWidgetNode w) e++ mkWidget w = w++ setEnabled (Widget w _) es = Widget w es
test/IllTyped.hs view
@@ -2,10 +2,10 @@ module Main (module Main) where -import AsyncRattus-import AsyncRattus.Signal as S+import WidgetRattus+import WidgetRattus.Signal as S import Prelude-import AsyncRattus.Plugin.Annotation (InternalAnn (..))+import WidgetRattus.Plugin.Annotation (InternalAnn (..)) {-# ANN loopIndirect ExpectError #-}
test/WellTyped.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE TypeOperators #-} {-# LANGUAGE StrictData #-}-{-# OPTIONS -fplugin=AsyncRattus.Plugin #-}+{-# OPTIONS -fplugin=WidgetRattus.Plugin #-} module Main (module Main) where -import AsyncRattus-import AsyncRattus.Signal+import WidgetRattus+import WidgetRattus.Signal import Data.Set as Set import Data.Text