我在哈斯克尔写作翻译。我想用monads做到这一点。
我已经创建了解析器,所以我有很多函数:: State -> MyMonad State
,我可以使用bind运行我的程序。 m >>= inst1 >>= inst2
。
一切都很好,但我不知道如何用我的语言创建指令print
(或read
)。
我不想要简单但丑陋的解决方案,例如保持字符串在State内打印并在最后打印main。 (如果我在打印时有无穷大怎么办?) 我无法从网上了解有关monad功能部分的文本。有一些解释,例如"包装在IO Monad中,它非常简单",但没有任何工作示例。几乎所有的印刷教程都是关于印刷的。
为了更好地解释问题,我准备了最少的翻译"例子(下)。 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
答案 0 :(得分:7)
为了创建一个带有Monad
实例且访问IO
形式的新类型,您需要创建另一个名为AutomatMT
的monad转换器类型并声明一个实例Monad
,MonadTrans
等等。它涉及很多样板代码。我会尝试澄清任何无意义的事情。
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 ()