diff --git a/backends/katip/benign-katip.cabal b/backends/katip/benign-katip.cabal index d871545..d7cc17a 100644 --- a/backends/katip/benign-katip.cabal +++ b/backends/katip/benign-katip.cabal @@ -1,12 +1,12 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0. +-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack name: benign-katip version: 0.1.0 -synopsis: A library for benign effects +synopsis: A Benign backend for the Katip library description: see README.md. homepage: https://github.com/aspiwack/haskell-benign#readme bug-reports: https://github.com/aspiwack/haskell-benign/issues diff --git a/backends/katip/package.yaml b/backends/katip/package.yaml index 6331ccb..627049c 100644 --- a/backends/katip/package.yaml +++ b/backends/katip/package.yaml @@ -4,7 +4,7 @@ author: Arnaud Spiwack maintainer: arnaud@spiwack.net github: aspiwack/haskell-benign copyright: MIT -synopsis: A library for benign effects +synopsis: A Benign backend for the Katip library description: see README.md. dependencies: diff --git a/backends/katip/src/Benign/Katip.hs b/backends/katip/src/Benign/Katip.hs index cdbfc77..484465e 100644 --- a/backends/katip/src/Benign/Katip.hs +++ b/backends/katip/src/Benign/Katip.hs @@ -1,6 +1,19 @@ {-# LANGUAGE GHC2021 #-} -module Benign.Katip where +-- | This module is a small wrapper around +-- [Katip](https://hackage.haskell.org/package/katip) to allow logging in pure +-- code. +-- +-- The wrapper is a little primitive still and you will have to handle the +-- transition from IO code to pure code manually. Pull requests are, of course, +-- welcome. +module Benign.Katip + ( withKatipContext, + withKatipNamespace, + withKatip, + logLocM, + ) +where import Benign qualified import GHC.Stack @@ -22,19 +35,13 @@ katipNamespace = unsafePerformIO Benign.newField -- | See 'Katip.katipAddContext'. withKatipContext :: (Katip.LogItem i) => i -> Benign.Strat a -> a -> a -withKatipContext item = Benign.withAltering katipContext addContext - where - addContext (Just st) = Just $ st <> Katip.liftPayload item - addContext Nothing = error "todo" +withKatipContext item = Benign.withAltering katipContext (<> Just (Katip.liftPayload item)) -- | See 'Katip.katipAddNamespace'. withKatipNamespace :: Katip.Namespace -> Benign.Strat a -> a -> a -withKatipNamespace namespace = Benign.withAltering katipNamespace addNamespace - where - addNamespace (Just st) = Just $ st <> namespace - addNamespace Nothing = error "todo" +withKatipNamespace namespace = Benign.withAltering katipNamespace (<> Just namespace) --- | Within this computation, Katip is configured. +-- | Within this computation, Katip is configured for pure code. withKatip :: (Katip.LogItem c) => Katip.LogEnv -> @@ -48,7 +55,9 @@ withKatip env ctx namespace strat = . Benign.withSettingIO' katipContext (Katip.liftPayload ctx) . Benign.withSettingIO katipNamespace namespace strat -logLocM :: forall a. (HasCallStack) => Katip.Severity -> Katip.LogStr -> Benign.Strat a -> a -> a +-- | @'logLocM' s msg a@ logs a an event, like Katip's 'Katip.logLocM', before +-- evaluating @a@. +logLocM :: forall a. (HasCallStack) => Katip.Severity -> Katip.LogStr -> a -> a logLocM severity str = withFrozenCallStack spanLog where -- The whole purpose of naming `span` is to freeze the call stack. It's @@ -59,16 +68,16 @@ logLocM severity str = withFrozenCallStack spanLog -- scratch. This would be invisible. I tried to harden this function by -- declaring type signatures everywhere. I haven't tested it yet though. It -- may be wrong. - spanLog :: HasCallStack => Benign.Strat a -> a -> a - spanLog = Benign.unsafeSpanBenign doLog (return ()) + spanLog :: (HasCallStack) => a -> a + spanLog = Benign.unsafeSpanBenign doLog (return ()) Benign.whnf - doLog :: HasCallStack => IO () + doLog :: (HasCallStack) => IO () doLog = do -- Making an intermediary `KatipContextT` is a little roundabout, but it's -- easier than reaching to Katip's internals. -- -- TODO: catch errors - Just env <- Benign.lookupLocalState katipEnv - Just ctx <- Benign.lookupLocalState katipContext - Just namespace <- Benign.lookupLocalState katipNamespace + Just env <- Benign.lookupLexicalState katipEnv + Just ctx <- Benign.lookupLexicalState katipContext + Just namespace <- Benign.lookupLexicalState katipNamespace Katip.runKatipContextT env ctx namespace $ Katip.logLocM severity str diff --git a/backends/timestats/benign-timestats.cabal b/backends/timestats/benign-timestats.cabal index a366ec7..6e4db62 100644 --- a/backends/timestats/benign-timestats.cabal +++ b/backends/timestats/benign-timestats.cabal @@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0. +-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack name: benign-timestats version: 0.1.0 -synopsis: A library for benign effects -description: see README.md. +synopsis: A Benign backend for the Timestats library +description: See the Benign library. homepage: https://github.com/aspiwack/haskell-benign#readme bug-reports: https://github.com/aspiwack/haskell-benign/issues author: Arnaud Spiwack diff --git a/backends/timestats/package.yaml b/backends/timestats/package.yaml index b94220a..d76e87a 100644 --- a/backends/timestats/package.yaml +++ b/backends/timestats/package.yaml @@ -4,8 +4,8 @@ author: Arnaud Spiwack maintainer: arnaud@spiwack.net github: aspiwack/haskell-benign copyright: MIT -synopsis: A library for benign effects -description: see README.md. +synopsis: A Benign backend for the Timestats library +description: See the Benign library. dependencies: - base diff --git a/backends/timestats/src/Benign/TimeStats.hs b/backends/timestats/src/Benign/TimeStats.hs index efca1f4..f08dec2 100644 --- a/backends/timestats/src/Benign/TimeStats.hs +++ b/backends/timestats/src/Benign/TimeStats.hs @@ -7,6 +7,8 @@ import Control.Exception import Debug.TimeStats qualified as TimeStats import System.IO.Unsafe (unsafePerformIO) +-- | @'measure' label strat thing@ measures, in the style of +-- 'TimeStats.measurePure', of running @strat thing@. measure :: String -> Benign.Strat a -> a -> a measure label strat thing = unsafePerformIO $ TimeStats.measureM label $ do Benign.E <- evaluate (strat thing); return thing {-# NOINLINE measure #-} diff --git a/benign.cabal b/benign.cabal index 770c6eb..58ccc67 100644 --- a/benign.cabal +++ b/benign.cabal @@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0. +-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack name: benign version: 0.1.0 synopsis: A library for benign effects -description: see README.md. +description: See the Benign library. homepage: https://github.com/aspiwack/haskell-benign#readme bug-reports: https://github.com/aspiwack/haskell-benign/issues author: Arnaud Spiwack diff --git a/examples/SimplePrint.hs b/examples/SimplePrint.hs index ab34b6e..c8f47cc 100644 --- a/examples/SimplePrint.hs +++ b/examples/SimplePrint.hs @@ -42,7 +42,7 @@ log log_line = Benign.unsafeSpanBenign do_log (return ()) where do_log :: IO () do_log = do - ctx <- Benign.lookupLocalState context + ctx <- Benign.lookupLexicalState context putStrLn (fromMaybe "" ctx ++ " " ++ log_line) ------------------------------------------------------------------------------ diff --git a/nix/sources.json b/nix/sources.json index 98bd791..ca5b319 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -5,10 +5,10 @@ "homepage": "https://github.com/NixOS/nixpkgs", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7f9be6a505a31f88499c5d20d11f98accf5ae6ba", - "sha256": "147wp515k1x08blm3nn2z994wbdnbl0vhp472ym1habfihfr7x65", + "rev": "2e1496bf8652ff4af4e4d4737277f71e4a4f5cb2", + "sha256": "0afb9y45zp2ikbzpbicy72l4dh9n0mgank8kqmyqxb8ib7s6qmsx", "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/7f9be6a505a31f88499c5d20d11f98accf5ae6ba.tar.gz", + "url": "https://github.com/NixOS/nixpkgs/archive/2e1496bf8652ff4af4e4d4737277f71e4a4f5cb2.tar.gz", "url_template": "https://github.com///archive/.tar.gz" } } diff --git a/package.yaml b/package.yaml index 490b8e1..94f34b6 100644 --- a/package.yaml +++ b/package.yaml @@ -5,7 +5,7 @@ maintainer: arnaud@spiwack.net github: aspiwack/haskell-benign copyright: MIT synopsis: A library for benign effects -description: see README.md. +description: See the Benign library. dependencies: - base diff --git a/shell.nix b/shell.nix index 63f3954..2e08f13 100644 --- a/shell.nix +++ b/shell.nix @@ -4,7 +4,7 @@ let sources = import ./nix/sources.nix; pkgs = import sources.nixpkgs { }; # ghc924 matches the version from Stack - ghcVersion = "924"; + ghcVersion = "984"; in let diff --git a/src/Benign.hs b/src/Benign.hs index 621e08e..6f10a9b 100644 --- a/src/Benign.hs +++ b/src/Benign.hs @@ -1,5 +1,5 @@ -{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE GHC2021 #-} +{-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE RoleAnnotations #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} @@ -14,17 +14,37 @@ -- real end. For tracing, for instance, you need to mark the end of -- evaluation. -- --- This library uses the 'Eval' type class to add a (programmable) +-- == Evaluation strategies +-- +-- This library uses the /evaluation strategies/ ('Strat') to add a (programmable) -- end to expressions. If you have a function @span "begin" "end"@ to log -- strings before and after an expression, you could write -- --- > span "begin" "end" $ Seq $ u +-- > span "begin" "end" whnf u +-- +-- To mean that @"end"@ is logged after evaluating `u` in weak +-- head normal form. 'whnf' is an evaluation strategies. Another one is 'nf' +-- +-- > span "begin" "end" nf u +-- +-- To mean that @"end"@ is logged after evaluating `u` in normal form, this +-- time. Most functions in this module take an evaluation strategy as an +-- argument. You can evaluate as much or as little as you want. -- --- To mean that `"end"` is logged after calling `seq u` (evaluating `u` in weak --- head normal form). `Seq` is called an /evaluation strategy/ for the purpose --- of this module. Evaluation strategies are all given as data type wrapper. +-- == Conventions +-- +-- * A convention in this module is that functions which act on monadic code +-- have a primed version (/e.g./ 'withAlteringM' and 'withAlteringM''). The +-- primed version doesn't take an evaluation strategy as an argument (the +-- evaluation strategy is effectively 'lazy'). Because quite often, in monadic +-- code, you want to span over just the monadic computation, not necessarily +-- the evaluation of the result. module Benign - ( Field, + ( unsafeSpanBenign, + + -- * Lexical state + -- $lexical-state + Field, newField, withAltering, withSetting, @@ -32,10 +52,9 @@ module Benign withAlteringIO', withSettingIO, withSettingIO', - lookupLocalState, - lookupLocalState', - lookupLocalStateWithDefault, - unsafeSpanBenign, + lookupLexicalState, + lookupLexicalState', + lookupLexicalStateWithDefault, -- * Monads EvalM (..), @@ -77,11 +96,51 @@ import GHC.Stack (HasCallStack) import System.IO.Unsafe import Unsafe.Coerce +-- $lexical-state The Benign library provides a lexical state facility. That is +-- a piece of state which is modified within a scope (like 'local' for the +-- 'Reader' monad), rather than for the rest of the program (like 'put' for the +-- 'State' monad). +-- +-- Why do we need this? If we are to do some logging in pure code, we still need +-- to know /where/ to log too. That is we need configuration. It is out of the +-- question to modify all the pure code to take the configuration as arguments +-- (either explicit, implicit, or with a monad): this would force us to pass +-- arguments down all the functions that call functions that ultimately call a +-- logging function. It would be majorly inconvenient, but mostly it goes +-- against the fact that the code is pure. If we go this way, we may as well +-- write all the code in monadic style. It is an assumption of this library that +-- we don't want to do this (and it seems to be supported by experience that +-- most Haskell programmers don't want to write most of their code in a monad). +-- +-- So we need a way to pass some state to pure code, to be used in benign +-- effects (see 'lookupLexicalState' and 'unsafeSpanBenign'). This is what +-- 'withAltering' (and friends) achieves. +-- +-- Another example of state which makes it especially obvious that we want a +-- lexical state rather that a global state: some logging frameworks, such as +-- Katip, let you add some context to logging strings. This context, of course, +-- is lexical (you don't modify the logging context in another thread to reflect +-- what's going on in your thread). +-- +-- At any rate, since functions like 'withAltering' are exposed as a pure +-- function, it wouldn't make much sense to modify a global state. The +-- modification could happen at any time and in any order. The result would be +-- quite ill-defined. + +-- The lexical state lets you pass some context to benign effects. For instance +-- in a logging application, you may want to know which span you are in. + +-- | A (typed) key which lets you retrieve data in the lexical state. newtype Field a = MkField Unique deriving newtype (Eq, Ord) type role Field nominal +-- | Create a new field. +-- +-- Fields are typically not exposed directly but only used internally to a +-- module to make sure that other module don't create inconsistent contexts for +-- you. newField :: IO (Field a) newField = MkField <$> Unique.newUnique @@ -93,7 +152,7 @@ lookupVault (MkField u) = unsafeCoerce $ Map.lookup u alterVault :: (Maybe a -> Maybe a) -> Field a -> Vault -> Vault alterVault f (MkField u) = Map.alter (unsafeCoerce f) u -type LocalStates = Map ThreadId Vault +type LexicalStates = Map ThreadId Vault -- Implementation note: the `Vault` is immutable data, because `localStates` is -- a lexical state rather than a global state: the state is modified within a @@ -108,77 +167,59 @@ type LocalStates = Map ThreadId Vault -- keeping in mind, though, that the value associated with a thread is never -- modified: only set (once) then deleted. So this is what the structure would -- have to recognise is non-interfering for STM actions. -localStates :: MVar LocalStates +localStates :: MVar LexicalStates localStates = unsafePerformIO $ newMVar Map.empty {-# NOINLINE localStates #-} -myLocalState :: IO Vault -myLocalState = do +myLexicalState :: IO Vault +myLexicalState = do tid <- myThreadId currentStates <- readMVar localStates return $ Map.findWithDefault Map.empty tid currentStates -lookupLocalState :: Field a -> IO (Maybe a) -lookupLocalState f = lookupVault f <$> myLocalState +-- | @'lookupLexicalState' f@ returns @Just a@ if @a@ is the (lexical) value of +-- field @f@ in the lexical state. It returns @Nothing@ is the field is unset. +lookupLexicalState :: Field a -> IO (Maybe a) +lookupLexicalState f = lookupVault f <$> myLexicalState -lookupLocalState' :: HasCallStack => Field a -> IO a -lookupLocalState' f = fromMaybe errmsg <$> lookupLocalState f +-- | @'lookupLexicalState' f@ returns @a@ if @a@ is the (lexical) value of +-- field @f@ in the lexical state. It throws an error if the field is unset. +lookupLexicalState' :: (HasCallStack) => Field a -> IO a +lookupLexicalState' f = fromMaybe errmsg <$> lookupLexicalState f where -- It would be nice if fields had a name that we could use here to display -- more helpful error messages. errmsg = error "Trying to retrieve an absent field" -lookupLocalStateWithDefault :: a -> Field a -> IO a -lookupLocalStateWithDefault deflt f = fromMaybe deflt <$> lookupLocalState f +-- | @'lookupLexicalState' d f@ returns @a@ if @a@ is the (lexical) value of +-- field @f@ in the lexical state. It returns @d@ if the field is unset. +lookupLexicalStateWithDefault :: a -> Field a -> IO a +lookupLexicalStateWithDefault deflt f = fromMaybe deflt <$> lookupLexicalState f -setLocalState :: Vault -> IO () -setLocalState vault = do +setLexicalState :: Vault -> IO () +setLexicalState vault = do tid <- myThreadId modifyMVar_ localStates (evaluate . Map.insert tid vault) --- | @'withAltering' f g strat thing@ evaluates 'thing' with the local state's +-- | @'withAltering' f g strat thing@ evaluates 'thing' with the lexical state's -- field `f` set by `g` in the style of 'Map.alter'. -- --- Why do we need this? If we are to do some logging in pure code, we still need --- to know /where/ to log too. That is we need configuration. It is out of --- question to modify all the pure code to take the configuration as arguments --- (either explicit, implicit, or with a monad): this would force us to pass --- arguments down all the functions that call functions that ultimately call a --- logging function. It would be majorly inconvenient, but mostly it goes --- against the fact that the code is pure. If we go this way, we may as well --- write all the code in monadic style. It is an assumption of this library that --- we don't want to do this (and it seems to be supported by experience that --- most Haskell programmers don't want to write most of their code in a monad). --- --- So we need a way to pass some state to pure code, to be used in benign --- effects (see 'lookupLocalState' and 'unsafeSpanBenign'). This is what --- 'withAltering' (and friends) achieves. --- --- Another example of state which makes it especially obvious that we want a --- lexical state (à la `Reader` monad) rather that a global state (à la `State` --- monad): some logging frameworks, such as Katip, let you add some context to --- logging strings. This context, of course, is lexical (you don't modify the --- logging context in another thread to reflect what's going on in your thread). --- --- At any rate, since 'withAltering' is exposed as a pure function, it wouldn't --- make much sense to modify a global state. The modification could happen at --- any time and in any order. The result would be quite ill-defined. --- -- The reason why we need the 'strat' argument is that, as a direct consequence -- of the design, the lexical state is passed dynamically to `thing`. That is, -- the state that is seen by a piece of code depends on where it's executed: if -- a lazy thunk escapes 'withAltering', then it's going to be picking up a -- different state. 'strat' lets us be deliberate about what escapes and what -- doesn't. Namely the altered state is available precisely during the --- evaluation of `strat thing`. +-- evaluation of @strat thing@. withAltering :: Field a -> (Maybe a -> Maybe a) -> Strat b -> b -> b withAltering f g strat thing = unsafePerformIO $ withAlteringIO f g strat (return thing) {-# NOINLINE withAltering #-} --- | TODO +-- | This lets you modify the lexical state in an 'IO' monad scope. The lexical +-- state is still shared. See also 'withAltering'. withAlteringIO :: Field a -> (Maybe a -> Maybe a) -> Strat b -> IO b -> IO b withAlteringIO f g strat thing = do - outer_vault <- myLocalState + outer_vault <- myLexicalState inner_vault <- evaluate $ alterVault g f outer_vault -- We make an `async` here, not because of concurrency: it's not going to be -- run concurrently with anything. It's because `myThreadId` is the only piece @@ -200,7 +241,7 @@ withAlteringIO f g strat thing = do -- - Very featureful implementation: https://hackage.haskell.org/package/context -- - Uses the GC to collect thread-local state: https://hackage.haskell.org/package/thread-utils-context me <- async $ do - setLocalState inner_vault + setLexicalState inner_vault r <- thing E <- evaluate $ strat r return r @@ -210,24 +251,23 @@ withAlteringIO f g strat thing = do Async.wait me `finally` modifyMVar_ localStates (evaluate . Map.delete (Async.asyncThreadId me)) --- | Like 'withAltering', but the `strat` is `lazy`. This is often the right --- choice in a monadic context. +-- | Like 'withAlteringIO', but the `strat` is 'lazy'. withAlteringIO' :: Field a -> (Maybe a -> Maybe a) -> IO b -> IO b withAlteringIO' f g = withAlteringIO f g lazy --- | @'withSetting f a thing@ evaluates 'thing' with the local state's field `f` +-- | @'withSetting' f a thing@ evaluates 'thing' with the local state's field `f` -- set to `a`. -- -- See 'withAltering' for more explanations. withSetting :: Field a -> a -> Strat b -> b -> b withSetting f a = withAltering f (\_ -> Just a) --- | TODO +-- | Sets the lexical state in an 'IO' context. See also 'withSetting' and +-- 'withAlteringIO'. withSettingIO :: Field a -> a -> Strat b -> IO b -> IO b withSettingIO f a = withAlteringIO f (\_ -> Just a) --- | Like 'withSetting', but the `strat` is `lazy`. This is often the right --- choice in a monadic context. +-- | Like 'withSettingIO', but the `strat` is 'lazy'. withSettingIO' :: Field a -> a -> IO b -> IO b withSettingIO' f a = withSettingIO f a lazy @@ -235,10 +275,14 @@ withSettingIO' f a = withSettingIO f a lazy -- evaluating `strat thing`, then runs the `after` action. -- -- 'unsafeSpanBenign' is not typically used directly in programs, but used to --- write safe benign-effect-spanning functions. +-- write safe benign-effect-spanning functions. It's a typical way of declaring +-- effects as benign. -- -- To call 'unsafeSpanBenign' safely, make sure that the `before` and `after` -- actions are indeed benign. +-- +-- An alternative to 'unsafeSpanBenign' is to simply call 'unsafePerformIO' +-- directly over a 'bracket'. See the source code for 'unsafeSpanBenign'. unsafeSpanBenign :: -- | Action to run before evaluation IO () -> @@ -273,29 +317,31 @@ unsafeSpanBenign before after strat thing = unsafePerformIO $ do class EvalM m where spliceEval :: (forall a. Strat a -> a -> a) -> Strat b -> m b -> m b +-- | @'withAlteringM f g strat thing@ works like @'withAltering' f g strat +-- thing@, except that @thing@ is a monadic action. The `strat` evaluates the +-- return value. @'withAlteringM f g strat thing@ spans over the evaluation. See +-- also 'EvalM'. withAlteringM :: (EvalM m) => Field a -> (Maybe a -> Maybe a) -> Strat b -> m b -> m b withAlteringM f g = spliceEval (withAltering f g) --- | Like 'withAlteringM', but the `strat` is `lazy`. This is often the right --- choice in a monadic context. +-- | Like 'withAlteringM', but the `strat` is 'lazy'. withAlteringM' :: (EvalM m) => Field a -> (Maybe a -> Maybe a) -> m b -> m b withAlteringM' f g = withAlteringM f g lazy --- | Like 'withSetting', but in a monadic context. The `strat` is `lazy`. This --- is often the right choice in a monadic context. +-- | Like 'withSetting', but in a monadic context. See also 'withAlteringM'. withSettingM :: (EvalM m) => Field a -> a -> Strat b -> m b -> m b withSettingM f a = withAlteringM f (\_ -> Just a) --- | Like 'withSettingM', but the `strat` is `lazy`. This is often the right --- choice in a monadic context. +-- | Like 'withSettingM', but the `strat` is 'lazy'. withSettingM' :: (EvalM m) => Field a -> a -> m b -> m b withSettingM' f a = withSettingM f a lazy +-- | Like 'unsafeSpanBenign', but in a monadic context. See also +-- 'withAlteringM'. unsafeSpanBenignM :: (EvalM m) => IO () -> IO () -> Strat a -> m a -> m a unsafeSpanBenignM before after = spliceEval (unsafeSpanBenign before after) --- | Like 'unsafeSpanBenignM', but the `strat` is `lazy`. This is often the --- right choice in a monadic context. +-- | Like 'unsafeSpanBenignM', but the `strat` is 'lazy'. unsafeSpanBenignM' :: (EvalM m) => IO () -> IO () -> m a -> m a unsafeSpanBenignM' before after = unsafeSpanBenignM before after lazy @@ -327,11 +373,12 @@ instance (EvalM m) => EvalM (ReaderT e m) where -- actually roughly the same type, but a little more modern. Making sure in -- particular that 'E', as a monoid, is strict, so that -- --- > 'foldMap' :: Foldable t => Strat a -> Strat (t a) +-- > foldMap :: Foldable t => Strat a -> Strat (t a) -- --- Evaluates all the position in a container. +-- Evaluates all the positions in a container. type Strat a = a -> E +-- | A unit type with a strict monoid instance. data E = E deriving stock (Eq, Ord, Enum, Bounded, Show, Read) @@ -392,10 +439,10 @@ instance Eval () where instance Eval Ordering where eval = (`seq` E) -instance Eval a => Eval (Maybe a) where +instance (Eval a) => Eval (Maybe a) where eval = foldMap eval -instance Eval a => Eval [a] where +instance (Eval a) => Eval [a] where eval = foldMap eval instance (Eval a, Eval b) => Eval (Either a b) where diff --git a/src/Benign/GhcEventsAnalyze.hs b/src/Benign/GhcEventsAnalyze.hs index c68b4c6..7904f4b 100644 --- a/src/Benign/GhcEventsAnalyze.hs +++ b/src/Benign/GhcEventsAnalyze.hs @@ -4,14 +4,18 @@ -- [ghc-events-analyze](https://hackage.haskell.org/package/ghc-events-analyze). -- -- TLDR: --- * Compile with `ghc -eventlog` --- * Run the program as `myprogram +RTS -l` --- * Observe the produced event log with `ghc-events-analyze ` +-- +-- * Compile with @$ ghc -eventlog@ +-- * Run the program as @$ myprogram +RTS -l@ +-- * Observe the produced event log with @$ ghc-events-analyze \@ module Benign.GhcEventsAnalyze where import Benign qualified import Debug.Trace (traceEventIO) +-- | @'event' name strat thing@ emits a @"START " ++ name@ event before +-- evaluating @strat thing@. And a @"END " ++ name@ event when the evaluation is +-- complete. See also 'Debug.Trace.traceEvent'. event :: String -> Benign.Strat a -> a -> a event event_name = Benign.unsafeSpanBenign diff --git a/stack.yaml b/stack.yaml index 4090b06..254f6f1 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: nightly-2022-10-30 +resolver: lts-23.22 packages: - . - backends/katip diff --git a/stack.yaml.lock b/stack.yaml.lock index eedcc45..7e972c1 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -1,7 +1,7 @@ # This file was autogenerated by Stack. # You should not edit this file by hand. # For more information, please see the documentation at: -# https://docs.haskellstack.org/en/stable/lock_files +# https://docs.haskellstack.org/en/stable/topics/lock_files packages: - completed: @@ -13,7 +13,7 @@ packages: hackage: timestats-0.1.0 snapshots: - completed: - sha256: eca327fb42f0942a994c95e8765e11ba6c416a6e73251745b480e207b9c6c274 - size: 646692 - url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2022/10/30.yaml - original: nightly-2022-10-30 + sha256: f8c8f95e57e436cee7c053b6f424e2c07a1020304a5583f3568cd9521443c340 + size: 683838 + url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/23/22.yaml + original: lts-23.22