Печать внутри монады - PullRequest
0 голосов
/ 15 мая 2018

Я пишу переводчик в Haskell. Я хочу сделать это с помощью монад. Я уже создал парсер, поэтому у меня много функций :: State -> MyMonad State, и я могу запустить свою программу, используя bind. m >>= inst1 >>= inst2. Все отлично работает, но я не знаю, как создать инструкцию print (или read) на моем языке с этой монадой.

Я не хочу простых, но безобразных решений, таких как сохранение строк для печати внутри State и печать в основном в конце. (Что, если у меня есть бесконечность, пока с печатью?) Я не мог понять тексты из Интернета об этой части функциональности монады. Были некоторые объяснения типа «упаковать в IO Monad, это довольно просто», но без каких-либо рабочих примеров. И почти все учебники по печати были посвящены печати в основном.

Чтобы лучше объяснить проблему, я подготовил минимальный пример "интерпретатора" (ниже). Там State это просто Int, моя монада 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

Моя главная цель - добавить print x внутрь magicPrint. Однако, если не сложнее, было бы неплохо иметь getLine.

Я изменил состояние в magicPrint, потому что у печати на моем языке есть побочные эффекты.

Я знаю, что мне нужно что-то с монадными преобразователями и, возможно, с MonadIO, но трудно найти какое-либо руководство с простым объяснением для начинающих. Поэтому я был бы очень признателен за расширение моего минимального примера кода для работы с принтами (и, возможно, getLine / other read Int) и некоторые пояснения к этому (возможно, со ссылками).

Функтор и Аппликативный код основаны на Определение новой монады в haskell не вызывает инстанции для Applicative

1 Ответ

0 голосов
/ 15 мая 2018

Чтобы создать новый тип с экземпляром Monad и получить доступ к форме IO внутри него, вам нужно будет создать другой тип монадного преобразователя с именем AutomatMT и объявить экземпляр Monad, * 1005.* и т. д. за это.Это включает в себя много стандартного кода.Я постараюсь прояснить все, что не имеет смысла.

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 ()
...