haskell-debugger-view 0.1.0.0 → 0.2.0.0
raw patch · 6 files changed
+104/−22 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- GHC.Debugger.View.Class: instance GHC.Internal.Read.Read GHC.Debugger.View.Class.VarValue
- GHC.Debugger.View.Class: instance GHC.Internal.Show.Show GHC.Debugger.View.Class.VarValue
+ GHC.Debugger.View.Class: [ProgramAp] :: forall a1 a. Program (a1 -> a) -> Program a1 -> Program a
+ GHC.Debugger.View.Class: [ProgramAskThunk] :: forall a1. a1 -> Program Bool
+ GHC.Debugger.View.Class: [ProgramBranch] :: forall a. Program Bool -> Program a -> Program a -> Program a
+ GHC.Debugger.View.Class: [PureProgram] :: forall a. a -> Program a
+ GHC.Debugger.View.Class: data Program a
+ GHC.Debugger.View.Class: ifP :: Program Bool -> Program a -> Program a -> Program a
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Int.Int16
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Int.Int32
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Int.Int64
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Int.Int8
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Word.Word16
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Word.Word32
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Word.Word64
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView GHC.Internal.Word.Word8
+ GHC.Debugger.View.Class: instance GHC.Debugger.View.Class.DebugView [a]
+ GHC.Debugger.View.Class: instance GHC.Internal.Base.Applicative GHC.Debugger.View.Class.Program
+ GHC.Debugger.View.Class: instance GHC.Internal.Base.Functor GHC.Debugger.View.Class.Program
+ GHC.Debugger.View.Class: isThunk :: a -> Program Bool
+ GHC.Debugger.View.Class: simpleValue :: String -> Bool -> VarValue
- GHC.Debugger.View.Class: VarFieldsIO :: [(IO String, VarFieldValue)] -> VarFieldsIO
+ GHC.Debugger.View.Class: VarFieldsIO :: Program [(IO String, VarFieldValue)] -> VarFieldsIO
- GHC.Debugger.View.Class: VarValue :: String -> Bool -> VarValue
+ GHC.Debugger.View.Class: VarValue :: Program String -> Bool -> VarValue
- GHC.Debugger.View.Class: VarValueIO :: IO String -> Bool -> VarValueIO
+ GHC.Debugger.View.Class: VarValueIO :: Program (IO String) -> Bool -> VarValueIO
- GHC.Debugger.View.Class: [varFieldsIO] :: VarFieldsIO -> [(IO String, VarFieldValue)]
+ GHC.Debugger.View.Class: [varFieldsIO] :: VarFieldsIO -> Program [(IO String, VarFieldValue)]
- GHC.Debugger.View.Class: [varValueIO] :: VarValueIO -> IO String
+ GHC.Debugger.View.Class: [varValueIO] :: VarValueIO -> Program (IO String)
- GHC.Debugger.View.Class: [varValue] :: VarValue -> String
+ GHC.Debugger.View.Class: [varValue] :: VarValue -> Program String
- GHC.Debugger.View.Class: debugFields :: DebugView a => a -> VarFields
+ GHC.Debugger.View.Class: debugFields :: DebugView a => a -> Program VarFields
Files
- CHANGELOG.md +10/−0
- haskell-debugger-view.cabal +1/−1
- src/GHC/Debugger/View/ByteString.hs +2/−2
- src/GHC/Debugger/View/Class.hs +85/−13
- src/GHC/Debugger/View/Containers.hs +4/−4
- src/GHC/Debugger/View/Text.hs +2/−2
CHANGELOG.md view
@@ -1,5 +1,15 @@ # Revision history for haskell-debugger-view +## 0.2.0.0 -- 2026-01-05++* Add support for more complex debug visualizations using the Program abstraction+ * API change: `debugValue` and `debugFields` now return `Program`-wrapped+ values, rather than `VarFields/VarValue` directly.+* Introduce new functions `isThunk` and `ifP` for conditional debugging behavior+* Enhance list visualization to show up to 50 forced elements+* Add improved handling for lazy values in debug views+* Update documentation and examples for custom DebugView instances+ ## 0.1.0.0 -- 2025-11-18 * First version. Released on an unsuspecting world.
haskell-debugger-view.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.12 name: haskell-debugger-view-version: 0.1.0.0+version: 0.2.0.0 license: BSD-3-Clause author: Matthew Pickering, Rodrigo Mesquita maintainer: matthewtpickering@gmail.com, rodrigo@well-typed.com
src/GHC/Debugger/View/ByteString.hs view
@@ -6,6 +6,6 @@ import qualified Data.ByteString as BS instance DebugView BS.ByteString where- debugValue t = VarValue (show t) False- debugFields _ = VarFields []+ debugValue t = simpleValue (show t) False+ debugFields _ = pure (VarFields [])
src/GHC/Debugger/View/Class.hs view
@@ -20,7 +20,16 @@ , VarValue(..) , VarFields(..) , VarFieldValue(..)+ , simpleValue ++ -- * A 'Program' can describe a more complicated visualisation method which+ -- can query some information from the debugger.+ , Program(..)+ , isThunk+ , ifP++ -- * Utilities -- -- | These can make it easier to write your own custom instances.@@ -40,6 +49,9 @@ ) where +import Data.Int+import Data.Word+ -- | Custom handling of debug terms (e.g. in the variables pane, or when -- inspecting a lazy variable) class DebugView a where@@ -60,17 +72,52 @@ -- -- This method should only be called to get the fields if the corresponding -- @'VarValue'@ has @'varExpandable' = True@.- debugFields :: a -> VarFields+ debugFields :: a -> Program VarFields +-- | The 'Program' abstraction allows more complicated 'DebugView' instances+-- to be constructed. The debugger will interpreter a 'Program' lazily when+-- determining how to display a variable.+--+-- At the moment the only interesting query when constructing a program is determining+-- if a value is already evaluated or not. This can be used to only display the evaluated+-- prefix of a list for example.+data Program a where+ -- | Lift a value to a program+ PureProgram :: a -> Program a+ -- | Program application+ ProgramAp :: Program (a -> b) -> Program a -> Program b+ -- | Evaluate the conditional, and branch on the result+ ProgramBranch :: Program Bool -> Program a -> Program a -> Program a+ -- | Is the value a thunk or evaluated?+ ProgramAskThunk :: a -> Program Bool++instance Functor Program where+ fmap f x = ProgramAp (PureProgram f) x++instance Applicative Program where+ pure = PureProgram+ fx <*> fy = ProgramAp fx fy++-- | Construct a 'VarValue' which doesn't require a 'Program'.+simpleValue :: String -> Bool -> VarValue+simpleValue s b = VarValue (pure s) b++-- | Construct a 'Program' which determines if 'a' is a thunk or not.+isThunk :: a -> Program Bool+isThunk = ProgramAskThunk++-- | Construct a program which branches+ifP :: Program Bool -> Program a -> Program a -> Program a+ifP = ProgramBranch+ -- | The representation of the value for some variable on the debugger data VarValue = VarValue { -- | The value to display inline for this variable- varValue :: String+ varValue :: Program String -- | Can this variable further be expanded (s.t. @'debugFields'@ is not null?) , varExpandable :: Bool }- deriving (Show, Read) -- | The representation for fields of a value which is expandable in the debugger newtype VarFields = VarFields@@ -108,11 +155,19 @@ newtype BoringTy a = BoringTy a instance Show a => DebugView (BoringTy a) where- debugValue (BoringTy x) = VarValue (show x) False- debugFields _ = VarFields []+ debugValue (BoringTy x) = simpleValue (show x) False+ debugFields _ = pure $ VarFields [] deriving via BoringTy Int instance DebugView Int+deriving via BoringTy Int8 instance DebugView Int8+deriving via BoringTy Int16 instance DebugView Int16+deriving via BoringTy Int32 instance DebugView Int32+deriving via BoringTy Int64 instance DebugView Int64 deriving via BoringTy Word instance DebugView Word+deriving via BoringTy Word8 instance DebugView Word8+deriving via BoringTy Word16 instance DebugView Word16+deriving via BoringTy Word32 instance DebugView Word32+deriving via BoringTy Word64 instance DebugView Word64 deriving via BoringTy Double instance DebugView Double deriving via BoringTy Float instance DebugView Float deriving via BoringTy Integer instance DebugView Integer@@ -120,31 +175,48 @@ deriving via BoringTy String instance DebugView String instance DebugView (a, b) where- debugValue _ = VarValue "( , )" True- debugFields (x, y) = VarFields+ debugValue _ = simpleValue "( , )" True+ debugFields (x, y) = pure $ VarFields [ ("fst", VarFieldValue x) , ("snd", VarFieldValue y) ] +-- | This instance will display up to the first 50 forced elements of a list.+instance {-# OVERLAPPABLE #-} DebugView [a] where+ debugValue [] = simpleValue "[]" False+ debugValue (_:_) = simpleValue "[...]" True+ debugFields v = VarFields <$> go 0 v+ where+ go :: Int -> [a] -> Program [(String, VarFieldValue)]+ go 50 xs = pure [("tail", VarFieldValue xs)]+ go _ [] = pure []+ go n (x:xs) = ((show n, VarFieldValue x) :) <$>+ (ifP (isThunk xs) (pure $ [("tail", VarFieldValue xs)])+ (go (n + 1) xs))+ -------------------------------------------------------------------------------- -- * (Internal) Wrappers required to call `evalStmt` on methods more easily -------------------------------------------------------------------------------- -- | Wrapper to make evaluating from debugger easier data VarValueIO = VarValueIO- { varValueIO :: IO String+ { varValueIO :: Program (IO String) , varExpandableIO :: Bool } debugValueIOWrapper :: DebugView a => a -> IO [VarValueIO] debugValueIOWrapper x = case debugValue x of VarValue str b ->- pure [VarValueIO (pure str) b]+ pure [VarValueIO (pure <$> str) b] newtype VarFieldsIO = VarFieldsIO- { varFieldsIO :: [(IO String, VarFieldValue)]+ { varFieldsIO :: Program [(IO String, VarFieldValue)] } debugFieldsIOWrapper :: DebugView a => a -> IO [VarFieldsIO]-debugFieldsIOWrapper x = case debugFields x of- VarFields fls ->- pure [VarFieldsIO [ (pure fl_s, b) | (fl_s, b) <- fls]]+debugFieldsIOWrapper x = pure [VarFieldsIO (toVarFieldsIO <$> (debugFields x))]++toVarFieldsIO :: VarFields -> [(IO String, VarFieldValue)]+toVarFieldsIO x =+ case x of+ VarFields fls -> [ (pure fl_s, b) | (fl_s, b) <- fls]+
src/GHC/Debugger/View/Containers.hs view
@@ -7,15 +7,15 @@ import qualified Data.Map as M instance DebugView (IM.IntMap a) where- debugValue _ = VarValue "IntMap" True- debugFields im = VarFields+ debugValue _ = simpleValue "IntMap" True+ debugFields im = pure $ VarFields [ (show k, VarFieldValue v) | (k, v) <- IM.toList im ] instance Show k => DebugView (M.Map k a) where- debugValue _ = VarValue "Map" True- debugFields m = VarFields+ debugValue _ = simpleValue "Map" True+ debugFields m = pure $ VarFields [ (show k, VarFieldValue v) | (k, v) <- M.toList m ]
src/GHC/Debugger/View/Text.hs view
@@ -6,6 +6,6 @@ import qualified Data.Text as T instance DebugView T.Text where- debugValue t = VarValue (show (T.unpack t)) False- debugFields _ = VarFields []+ debugValue t = simpleValue (show (T.unpack t)) False+ debugFields _ = pure $ VarFields []