在 monad 中打印

Printing inside monad

我正在 haskell 中编写解释器。我想用 monad 做到这一点。 我已经创建了解析器,所以我有很多函数 :: State -> MyMonad State,我可以 运行 我的程序使用绑定。 m >>= inst1 >>= inst2。 一切正常,但我不知道如何用我的语言用那个 monad 创建指令 print(或 read)。

我不想要简单但丑陋的解决方案,例如让字符串在 State 内打印并最后在 main 中打印。 (如果打印时我有无穷大怎么办?) 我无法理解来自网络的关于 monad 功能的那部分的文本。有一些解释,如 "pack inside IO Monad, it's quite straightforward",但没有任何工作示例。几乎所有的打印教程都是关于在 main 中打印的。

为了更好地解释问题,我准备了最小的 "interpreter" 示例(如下)。 State 只是 Int,我的 monad 是 AutomatM 指令类型为 :: Int -> AutomatM Int。所以可能的指令是:

inc :: Int -> AutomatM Int
inc x = return (x+1)

我设计得尽可能简单:

import Control.Applicative
import Control.Monad (liftM, ap)
import Control.Monad.IO.Class (MonadIO(..))
import System.IO

data AutomatM a = AutomatError | Running a

instance Show a => Show (AutomatM a) where
    show (AutomatError) = "AutomatError"
    show (Running a) = "Running " ++ show a

instance Functor AutomatM where
  fmap = liftM

instance Applicative AutomatM where
  pure  = return
  (<*>) = ap


instance Monad AutomatM where
  return x = Running x
  m >>= g = case m of
              AutomatError -> AutomatError
              Running x -> g x
magicPrint x = do
    -- print x         -- How can I make print work?
    -- c <- getLine    -- And if that is as simple as print
    b <- return "1000" -- how can I change constant to c?
    return (x + (read b :: Int))

main = do
    a <- getLine
    print $ (Running (read a :: Int)) >>= (\x -> return (x*2)) >>= magicPrint

我的主要目标是在magicPrint中添加print x。但是,如果它不难,那么有 getLine 就好了。

我在 magicPrint 中更改了状态,因为用我的语言打印有副作用。

我知道我需要一些关于 monad 转换器和 MonadIO 的东西,但是很难找到任何对初学者有简单解释的教程。 因此,我非常感谢扩展我的最小代码示例以处理打印(可能 getLine/other 读取 Int)和一些解释(可能带有链接)。

Functor 和 Aplicative 代码基于

为了创建一个带有 Monad 实例的新类型并在其中访问 IO 表单,您需要创建另一个名为 AutomatMT 的 monad 转换器类型并声明一个MonadMonadTrans 等的实例。它涉及很多样板代码。我会尽力澄清任何没有意义的事情。

import Control.Applicative
import Control.Monad (liftM, ap)
import Control.Monad.IO.Class (MonadIO(..))
import System.IO
import Control.Monad.Trans.Class (MonadTrans(..), lift)

data AutomatM a = AutomatError | Running a

instance Show a => Show (AutomatM a) where
    show (AutomatError) = "AutomatError"
    show (Running a) = "Running " ++ show a

instance Functor AutomatM where
  fmap = liftM

instance Applicative AutomatM where
  pure  = return
  (<*>) = ap

instance Monad AutomatM where
  return x = Running x
  m >>= g = case m of
              AutomatError -> AutomatError
              Running x -> g x

newtype AutomatMT m a = AutomatMT { runAutomatMT :: m (AutomatM a) }

mapAutomatMT :: (m (AutomatM a) -> n (AutomatM b)) -> AutomatMT m a -> AutomatMT n b
mapAutomatMT f = AutomatMT . f . runAutomatMT

instance (Functor m) => Functor (AutomatMT m) where
    fmap f = mapAutomatMT (fmap (fmap f))

instance MonadTrans AutomatMT where
    lift = AutomatMT . liftM Running

instance (Functor m, Monad m) => Applicative (AutomatMT m) where
    pure = AutomatMT . return . Running

    mf <*> mx = AutomatMT $ do
        mb_f <- runAutomatMT mf
        case mb_f of
            AutomatError -> return AutomatError
            Running f  -> do
                mb_x <- runAutomatMT mx
                case mb_x of
                    AutomatError -> return AutomatError
                    Running x  -> return (Running (f x))

instance (MonadIO m) => MonadIO (AutomatMT m) where
    liftIO = lift . liftIO

instance (Monad m) => Monad (AutomatMT m) where
    x >>= f = AutomatMT $ do
        v <- runAutomatMT x
        case v of
            AutomatError -> return AutomatError
            Running y  -> runAutomatMT (f y)

    fail _ = AutomatMT (return AutomatError)


magicPrint :: String -> (AutomatMT IO String)
magicPrint x = do
  liftIO $ print $ "You gave magic print " ++ x
  let x = "12"
  y <- pure 1
  liftIO $ print y
  pure $ "1"

main = do
  print "Enter some text"
  a <- getLine
  b <- runAutomatMT $ magicPrint a
  pure ()