Type class for parser monads with debugging
Lev135 opened this issue · 4 comments
As it is mentioned in docs
it's not possible to lift this function into some monad transformers without introducing surprising behavior (e.g. unexpected state backtracking) or adding otherwise redundant constraints (e.g. Show instance for state), so this helper is only available for ParsecT monad, not any instance of MonadParsec in general
However, there are cases, when it would be very useful. For example for debugging parser with some state:
xCounter :: (MonadParsec Void String m, MonadState Depth m) => m Int
I propose to add special class (something like MonadParsecDbg
) and instances for it with "redudant constraints", i. e. Show
instance for state and writer's log type. This also gives us very useful state logging in debug mode (if we consider composed transformer m
as a parser with state, it seems very natural for me, that it is printed along with parser state).
Some example of how it can be used
Suggested solution:
class Monad m => MonadParsecDbg m where
dbg :: Show a => String -> m a -> m a
instance (VisualStream s, ShowErrorComponent e) => MonadParsecDbg (ParsecT e s m) where
dbg = Text.Megaparsec.Debug.dbg
instance (Show s, MonadParsecDbg m) => MonadParsecDbg (StateT s m) where
dbg str sma = StateT $ \s ->
dbg str $ runStateT sma s
instance (Show w, Monoid w, MonadParsecDbg m) => MonadParsecDbg (WriterT w m) where
dbg str wma = WriterT $ dbg str $ runWriterT wma
instance (MonadParsecDbg m) => MonadParsecDbg (ReaderT e m) where
dbg str rma = ReaderT $ \e -> dbg str $ runReaderT rma e
Maybe it would be better to add MonadParsec
constraint for class and e
, s
parameters. Here I've simplified it for to make it more readable.
I think it is a great idea. Would you like to turn it into a PR?
@mrkkrp Yes I'd like to implement it. As I understand, it should be located in Text.Megaparsec.Debug
. However, I'm not sure about some points:
- Can we just move
dbg
in class without preserving it's global declaration? As far as I can see, nothing should be broken by this change, but maybe I don't see some problem. - Also, not everything is clear with
dbg'
function. Should it also be included inMonadParsecDbg
class? In this case it will print parser state/log (with show constraints onStateT
andWriterT
arguments). I think it's reasonable, because we can parse some unshowable value, but have normal showable state. However, it's may be more consistent not to addShow
constraint indbg'
realization and ignore state/log just like returned value. In this case we'll need another class fordbg'
function (with no constraints).
Text.Megaparsec.Debug
is a good place for this new type class IMO.dbg
becomes a method of the new type class, I do not see any problems with this. Both the new type class and its methods are exposed in the API.- I think for the first iteration
dbg'
could be just defined in terms ofdbg
without becoming a method ofMonadPasecDbg
. One more type class in overkill, let's have theShow
constraints on state/accumulator and havedbg'
with itsBlind
wrapper handle unshowable things.
Wonderful. I was having trouble figuring out why 9.2.2 dbg didn't work with my (StateT ...) parsers. 9.3 dbg just works and is super useful, thanks!