在monad

时间:2018-05-15 00:01:29

标签: haskell functional-programming monads monad-transformers

我在哈斯克尔写作翻译。我想用monads做到这一点。 我已经创建了解析器,所以我有很多函数:: State -> MyMonad State,我可以使用bind运行我的程序。 m >>= inst1 >>= inst2。 一切都很好,但我不知道如何用我的语言创建指令print(或read)。

我不想要简单但丑陋的解决方案,例如保持字符串在State内打印并在最后打印main。 (如果我在打印时有无穷大怎么办?) 我无法从网上了解有关monad功能部分的文本。有一些解释,例如"包装在IO Monad中,它非常简单",但没有任何工作示例。几乎所有的印刷教程都是关于印刷的。

为了更好地解释问题,我准备了最少的翻译&#34;例子(下)。 State只有Int,我的monad AutomatM说明类型为:: Int -> AutomatM Int。所以可能的指示是:<​​/ p>

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

我的主要目标是在print x内添加magicPrint。但是,如果它不是更难,那么拥有getLine会很好。

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

我知道我需要monad变换器和MonadIO的东西,但是很难找到任何有初学者简单解释的教程。 因此,我非常感谢扩展我的最小代码示例以使用print(也许是getLine /其他读取Int)以及对它的一些解释(可能带有链接)。

Functor和Aplicative代码基于Defining a new monad in haskell raises no instance for Applicative

1 个答案:

答案 0 :(得分:7)

为了创建一个带有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 ()