{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Evaluation.Debug
( test_debug
) where
import PlutusCore.Evaluation.Machine.ExBudgetingDefaults
import PlutusCore.Pretty
import PlutusPrelude
import UntypedPlutusCore
import UntypedPlutusCore.Evaluation.Machine.SteppableCek.DebugDriver
import UntypedPlutusCore.Evaluation.Machine.SteppableCek.Internal
import Control.Monad.Reader
import Control.Monad.ST
import Control.Monad.Writer
import Data.ByteString.Lazy.Char8 qualified as BS
import Data.Text qualified as T
import Data.Void
import Prettyprinter
import Test.Tasty
import Test.Tasty.Golden
import UntypedPlutusCore.Evaluation.Machine.Cek
test_debug :: TestTree
test_debug :: TestTree
test_debug = [Char] -> [TestTree] -> TestTree
testGroup [Char]
"debug" ([TestTree] -> TestTree) -> [TestTree] -> TestTree
forall a b. (a -> b) -> a -> b
$
(([Char], [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)
-> TestTree)
-> [([Char], [Cmd Breakpoints],
NTerm DefaultUni DefaultFun EmptyAnn)]
-> [TestTree]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ([Char], [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)
-> TestTree
goldenVsDebug [([Char], [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)]
examples
type Breakpoints = Void
newtype EmptyAnn = EmptyAnn ()
deriving newtype (NonEmpty EmptyAnn -> EmptyAnn
EmptyAnn -> EmptyAnn -> EmptyAnn
(EmptyAnn -> EmptyAnn -> EmptyAnn)
-> (NonEmpty EmptyAnn -> EmptyAnn)
-> (forall b. Integral b => b -> EmptyAnn -> EmptyAnn)
-> Semigroup EmptyAnn
forall b. Integral b => b -> EmptyAnn -> EmptyAnn
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
$c<> :: EmptyAnn -> EmptyAnn -> EmptyAnn
<> :: EmptyAnn -> EmptyAnn -> EmptyAnn
$csconcat :: NonEmpty EmptyAnn -> EmptyAnn
sconcat :: NonEmpty EmptyAnn -> EmptyAnn
$cstimes :: forall b. Integral b => b -> EmptyAnn -> EmptyAnn
stimes :: forall b. Integral b => b -> EmptyAnn -> EmptyAnn
Semigroup, Semigroup EmptyAnn
EmptyAnn
Semigroup EmptyAnn =>
EmptyAnn
-> (EmptyAnn -> EmptyAnn -> EmptyAnn)
-> ([EmptyAnn] -> EmptyAnn)
-> Monoid EmptyAnn
[EmptyAnn] -> EmptyAnn
EmptyAnn -> EmptyAnn -> EmptyAnn
forall a.
Semigroup a =>
a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
$cmempty :: EmptyAnn
mempty :: EmptyAnn
$cmappend :: EmptyAnn -> EmptyAnn -> EmptyAnn
mappend :: EmptyAnn -> EmptyAnn -> EmptyAnn
$cmconcat :: [EmptyAnn] -> EmptyAnn
mconcat :: [EmptyAnn] -> EmptyAnn
Monoid)
instance Breakpointable EmptyAnn Breakpoints where
hasBreakpoints :: EmptyAnn -> Breakpoints -> Bool
hasBreakpoints EmptyAnn
_ = Breakpoints -> Bool
forall a. Breakpoints -> a
absurd
examples :: [(String, [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)]
examples :: [([Char], [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)]
examples = [
([Char]
"ex1", Cmd Breakpoints -> [Cmd Breakpoints]
forall a. a -> [a]
repeat Cmd Breakpoints
forall bps. Cmd bps
Step, EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann.
ann -> Term name uni fun ann -> Term name uni fun ann
Delay EmptyAnn
forall a. Monoid a => a
mempty (NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn)
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall a b. (a -> b) -> a -> b
$ EmptyAnn -> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann. ann -> Term name uni fun ann
Error EmptyAnn
forall a. Monoid a => a
mempty)
, ([Char]
"ex2", Int -> Cmd Breakpoints -> [Cmd Breakpoints]
forall a. Int -> a -> [a]
replicate Int
4 Cmd Breakpoints
forall bps. Cmd bps
Step, EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann.
ann -> Term name uni fun ann -> Term name uni fun ann
Force EmptyAnn
forall a. Monoid a => a
mempty (NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn)
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall a b. (a -> b) -> a -> b
$ EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann.
ann -> Term name uni fun ann -> Term name uni fun ann
Delay EmptyAnn
forall a. Monoid a => a
mempty (NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn)
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall a b. (a -> b) -> a -> b
$ EmptyAnn -> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann. ann -> Term name uni fun ann
Error EmptyAnn
forall a. Monoid a => a
mempty)
, ([Char]
"ex3", Int -> Cmd Breakpoints -> [Cmd Breakpoints]
forall a. Int -> a -> [a]
replicate Int
5 Cmd Breakpoints
forall bps. Cmd bps
Step, EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann.
ann -> Term name uni fun ann -> Term name uni fun ann
Force EmptyAnn
forall a. Monoid a => a
mempty (NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn)
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall a b. (a -> b) -> a -> b
$ EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann.
ann -> Term name uni fun ann -> Term name uni fun ann
Delay EmptyAnn
forall a. Monoid a => a
mempty (NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn)
-> NTerm DefaultUni DefaultFun EmptyAnn
-> NTerm DefaultUni DefaultFun EmptyAnn
forall a b. (a -> b) -> a -> b
$ EmptyAnn -> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann. ann -> Term name uni fun ann
Error EmptyAnn
forall a. Monoid a => a
mempty)
, ([Char]
"ex4", Cmd Breakpoints -> [Cmd Breakpoints]
forall a. a -> [a]
repeat Cmd Breakpoints
forall bps. Cmd bps
Step, EmptyAnn -> NTerm DefaultUni DefaultFun EmptyAnn
forall name (uni :: * -> *) fun ann. ann -> Term name uni fun ann
Error EmptyAnn
forall a. Monoid a => a
mempty)
]
goldenVsDebug :: (TestName, [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn) -> TestTree
goldenVsDebug :: ([Char], [Cmd Breakpoints], NTerm DefaultUni DefaultFun EmptyAnn)
-> TestTree
goldenVsDebug ([Char]
name, [Cmd Breakpoints]
cmds, NTerm DefaultUni DefaultFun EmptyAnn
term) =
[Char] -> [Char] -> IO ByteString -> TestTree
goldenVsString [Char]
name
([Char]
"untyped-plutus-core/test/Evaluation/Debug/" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
name [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
".golden")
(ByteString -> IO ByteString
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> IO ByteString) -> ByteString -> IO ByteString
forall a b. (a -> b) -> a -> b
$ [Char] -> ByteString
BS.pack ([Char] -> ByteString) -> [Char] -> ByteString
forall a b. (a -> b) -> a -> b
$ [[Char]] -> [Char]
unlines ([[Char]] -> [Char]) -> [[Char]] -> [Char]
forall a b. (a -> b) -> a -> b
$ [Cmd Breakpoints]
-> NTerm DefaultUni DefaultFun EmptyAnn -> [[Char]]
mock [Cmd Breakpoints]
cmds NTerm DefaultUni DefaultFun EmptyAnn
term)
mock :: [Cmd Breakpoints]
-> NTerm DefaultUni DefaultFun EmptyAnn
-> [String]
mock :: [Cmd Breakpoints]
-> NTerm DefaultUni DefaultFun EmptyAnn -> [[Char]]
mock [Cmd Breakpoints]
cmds NTerm DefaultUni DefaultFun EmptyAnn
t = (forall s. ST s [[Char]]) -> [[Char]]
forall a. (forall s. ST s a) -> a
runST ((forall s. ST s [[Char]]) -> [[Char]])
-> (forall s. ST s [[Char]]) -> [[Char]]
forall a b. (a -> b) -> a -> b
$ CekM Any Any s [[Char]] -> ST s [[Char]]
forall (uni :: * -> *) fun s a. CekM uni fun s a -> ST s a
unCekM (CekM Any Any s [[Char]] -> ST s [[Char]])
-> CekM Any Any s [[Char]] -> ST s [[Char]]
forall a b. (a -> b) -> a -> b
$ do
(CekTrans DefaultUni DefaultFun EmptyAnn s
cekTrans,ExBudgetInfo RestrictingSt DefaultUni DefaultFun s
_) <- MachineParameters
CekMachineCosts
DefaultFun
(CekValue DefaultUni DefaultFun EmptyAnn)
-> ExBudgetMode RestrictingSt DefaultUni DefaultFun
-> EmitterMode DefaultUni DefaultFun
-> Slippage
-> CekM
Any
Any
s
(CekTrans DefaultUni DefaultFun EmptyAnn s,
ExBudgetInfo RestrictingSt DefaultUni DefaultFun s)
forall cost (uni :: * -> *) fun ann (m :: * -> *) s.
(ThrowableBuiltins uni fun, PrimMonad m, s ~ PrimState m) =>
MachineParameters CekMachineCosts fun (CekValue uni fun ann)
-> ExBudgetMode cost uni fun
-> EmitterMode uni fun
-> Slippage
-> m (CekTrans uni fun ann s, ExBudgetInfo cost uni fun s)
mkCekTrans MachineParameters
CekMachineCosts
DefaultFun
(CekValue DefaultUni DefaultFun EmptyAnn)
forall ann.
Typeable ann =>
MachineParameters
CekMachineCosts DefaultFun (CekValue DefaultUni DefaultFun ann)
defaultCekParametersForTesting
ExBudgetMode RestrictingSt DefaultUni DefaultFun
forall (uni :: * -> *) fun.
ThrowableBuiltins uni fun =>
ExBudgetMode RestrictingSt uni fun
restrictingEnormous EmitterMode DefaultUni DefaultFun
forall (uni :: * -> *) fun. EmitterMode uni fun
noEmitter Slippage
defaultSlippage
WriterT [[Char]] (CekM Any Any s) () -> CekM Any Any s [[Char]]
forall (m :: * -> *) w a. Monad m => WriterT w m a -> m w
execWriterT (WriterT [[Char]] (CekM Any Any s) () -> CekM Any Any s [[Char]])
-> WriterT [[Char]] (CekM Any Any s) () -> CekM Any Any s [[Char]]
forall a b. (a -> b) -> a -> b
$ (ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
-> [Cmd Breakpoints] -> WriterT [[Char]] (CekM Any Any s) ())
-> [Cmd Breakpoints]
-> ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
-> WriterT [[Char]] (CekM Any Any s) ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
-> [Cmd Breakpoints] -> WriterT [[Char]] (CekM Any Any s) ()
forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT [Cmd Breakpoints]
cmds (ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
-> WriterT [[Char]] (CekM Any Any s) ())
-> ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
-> WriterT [[Char]] (CekM Any Any s) ()
forall a b. (a -> b) -> a -> b
$
(DebugF
DefaultUni
DefaultFun
EmptyAnn
Breakpoints
(ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ())
-> ReaderT
[Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ())
-> Free (DebugF DefaultUni DefaultFun EmptyAnn Breakpoints) ()
-> ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
forall (f :: * -> *) (m :: * -> *) a.
(Functor f, Monad m) =>
(f (m a) -> m a) -> Free f a -> m a
iterM (CekTrans DefaultUni DefaultFun EmptyAnn s
-> DebugF
DefaultUni
DefaultFun
EmptyAnn
Breakpoints
(ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ())
-> ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
forall (uni :: * -> *) fun s (m :: * -> *).
(ThrowableBuiltins uni fun, MonadWriter [[Char]] m,
MonadReader [Cmd Breakpoints] m, PrimMonad m, PrimState m ~ s) =>
CekTrans uni fun EmptyAnn s
-> DebugF uni fun EmptyAnn Breakpoints (m ()) -> m ()
handle CekTrans DefaultUni DefaultFun EmptyAnn s
cekTrans) (Free (DebugF DefaultUni DefaultFun EmptyAnn Breakpoints) ()
-> ReaderT
[Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ())
-> Free (DebugF DefaultUni DefaultFun EmptyAnn Breakpoints) ()
-> ReaderT [Cmd Breakpoints] (WriterT [[Char]] (CekM Any Any s)) ()
forall a b. (a -> b) -> a -> b
$ NTerm DefaultUni DefaultFun EmptyAnn
-> Free (DebugF DefaultUni DefaultFun EmptyAnn Breakpoints) ()
forall (uni :: * -> *) fun ann bps (m :: * -> *).
(Breakpointable ann bps, MonadFree (DebugF uni fun ann bps) m) =>
NTerm uni fun ann -> m ()
runDriverT NTerm DefaultUni DefaultFun EmptyAnn
t
handle :: forall uni fun s m.
( ThrowableBuiltins uni fun
, MonadWriter [String] m, MonadReader [Cmd Breakpoints] m
, PrimMonad m, PrimState m ~ s
)
=> CekTrans uni fun EmptyAnn s
-> DebugF uni fun EmptyAnn Breakpoints (m ()) -> m ()
handle :: forall (uni :: * -> *) fun s (m :: * -> *).
(ThrowableBuiltins uni fun, MonadWriter [[Char]] m,
MonadReader [Cmd Breakpoints] m, PrimMonad m, PrimState m ~ s) =>
CekTrans uni fun EmptyAnn s
-> DebugF uni fun EmptyAnn Breakpoints (m ()) -> m ()
handle CekTrans uni fun EmptyAnn s
cekTrans = \case
StepF CekState uni fun EmptyAnn
prevState CekState uni fun EmptyAnn -> m ()
k -> do
Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn)
eNewState <- CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
-> m (Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
forall (m :: * -> *) s (uni :: * -> *) fun a.
(PrimMonad m, PrimState m ~ s) =>
CekM uni fun s a -> m a
liftCek (CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
-> m (Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn)))
-> CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
-> m (Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
forall a b. (a -> b) -> a -> b
$ CekM uni fun s (CekState uni fun EmptyAnn)
-> CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
forall e (m :: * -> *) a. MonadError e m => m a -> m (Either e a)
tryError (CekM uni fun s (CekState uni fun EmptyAnn)
-> CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn)))
-> CekM uni fun s (CekState uni fun EmptyAnn)
-> CekM
uni
fun
s
(Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn))
forall a b. (a -> b) -> a -> b
$ CekTrans uni fun EmptyAnn s
cekTrans CekState uni fun EmptyAnn
prevState
case Either
(CekEvaluationException NamedDeBruijn uni fun)
(CekState uni fun EmptyAnn)
eNewState of
Right CekState uni fun EmptyAnn
newState -> do
[[Char]] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [Doc Any -> [Char]
forall a. Show a => a -> [Char]
show (Doc Any -> [Char]) -> Doc Any -> [Char]
forall a b. (a -> b) -> a -> b
$ Doc Any
"OldState:" Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> CekState uni fun EmptyAnn -> Doc Any
forall a ann. Pretty a => a -> Doc ann
forall ann. CekState uni fun EmptyAnn -> Doc ann
pretty CekState uni fun EmptyAnn
prevState
Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc Any
"NewState:" Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> CekState uni fun EmptyAnn -> Doc Any
forall a ann. Pretty a => a -> Doc ann
forall ann. CekState uni fun EmptyAnn -> Doc ann
pretty CekState uni fun EmptyAnn
newState]
CekState uni fun EmptyAnn -> m ()
k CekState uni fun EmptyAnn
newState
Left CekEvaluationException NamedDeBruijn uni fun
e -> [[Char]] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [Doc Any -> [Char]
forall a. Show a => a -> [Char]
show (Doc Any -> [Char]) -> Doc Any -> [Char]
forall a b. (a -> b) -> a -> b
$ Doc Any
"OldState:" Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> CekState uni fun EmptyAnn -> Doc Any
forall a ann. Pretty a => a -> Doc ann
forall ann. CekState uni fun EmptyAnn -> Doc ann
pretty CekState uni fun EmptyAnn
prevState
Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> Doc Any
"NewState is Error:" Doc Any -> Doc Any -> Doc Any
forall ann. Doc ann -> Doc ann -> Doc ann
<+> CekEvaluationException NamedDeBruijn uni fun -> Doc Any
forall a ann. Show a => a -> Doc ann
viaShow CekEvaluationException NamedDeBruijn uni fun
e]
InputF Cmd Breakpoints -> m ()
k -> (Cmd Breakpoints -> m ()) -> m ()
handleInput Cmd Breakpoints -> m ()
k
DriverLogF Text
text m ()
k -> [Char] -> m ()
handleLog (Text -> [Char]
T.unpack Text
text) m () -> m () -> m ()
forall a b. m a -> m b -> m b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> m ()
k
UpdateClientF CekState uni fun EmptyAnn
_ m ()
k -> m ()
k
where
handleInput :: (Cmd Breakpoints -> m ()) -> m ()
handleInput :: (Cmd Breakpoints -> m ()) -> m ()
handleInput Cmd Breakpoints -> m ()
k = do
[Cmd Breakpoints]
cmds <- m [Cmd Breakpoints]
forall r (m :: * -> *). MonadReader r m => m r
ask
case [Cmd Breakpoints]
cmds of
[] ->
[[Char]] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell [[Char]
"Early run out of commands"]
(Cmd Breakpoints
cmd:[Cmd Breakpoints]
cmds') ->
([Cmd Breakpoints] -> [Cmd Breakpoints]) -> m () -> m ()
forall a. ([Cmd Breakpoints] -> [Cmd Breakpoints]) -> m a -> m a
forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local ([Cmd Breakpoints] -> [Cmd Breakpoints] -> [Cmd Breakpoints]
forall a b. a -> b -> a
const [Cmd Breakpoints]
cmds') (m () -> m ()) -> m () -> m ()
forall a b. (a -> b) -> a -> b
$
Cmd Breakpoints -> m ()
k Cmd Breakpoints
cmd
handleLog :: String -> m ()
handleLog :: [Char] -> m ()
handleLog = [[Char]] -> m ()
forall w (m :: * -> *). MonadWriter w m => w -> m ()
tell ([[Char]] -> m ()) -> ([Char] -> [[Char]]) -> [Char] -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Char] -> [[Char]]
forall a. a -> [a]
forall (f :: * -> *) a. Applicative f => a -> f a
pure