【问题标题】:Interpreting the Teletype free monad in the RWS monad在 RWS monad 中解释 Teletype free monad
【发布时间】:2018-10-18 22:23:53
【问题描述】:

我目前正在学习免费的 monad,我正在玩弄可能是最简单和最常见的例子 - Teletype:

{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.Free

data TeletypeF a = Put String a
                 | Get (String -> a)
    deriving Functor

type Teletype = Free TeletypeF

许多教程解释IO monad 中的Teletype 程序。例如:

-- Utilities
get   = liftF $ Get id
put s = liftF $ Put s ()

-- Sample programs
echo :: Teletype ()
echo = do word <- get
          if word == "\04"  -- Ctrl-D
          then return ()
          else put word >> echo

hello :: Teletype ()
hello = do put "What is your name?"
           name <- get
           put "What is your age?"
           age <- get
           put ("Hello, " ++ name ++ "!")
           put ("You are " ++ age ++ " years old!")

-- Interpret to IO
interpIO :: Teletype a -> IO a
interpIO = foldFree lift
    where
        lift (Put s a) = putStrLn s >> return a
        lift (Get f)   = getLine >>= return . f

我试图用不同的 monad 来解释它,即 RWS monad。 这个想法是由this assignment 的最后一个练习激发的。 我正在使用RWS 数据类型从Reader 部分获取输入并在State 部分中累积输出。 但是,不幸的是,我无法让它工作。到目前为止,这是我的尝试:

import Control.Monad.Trans.RWS.Lazy hiding (get, put)

type TeletypeRWS = RWS [String] () [String]

-- Interpret to TeletypeRWS
interpRWS :: Teletype a -> TeletypeRWS a
interpRWS = foldFree lift
    where
        lift (Put s a) = state (\t -> ((), t ++ [s])) >> return a
        lift (Get f)   = reader head >>= local tail . return . f  -- This is wrong

mockConsole :: Teletype a -> [String] -> (a, [String])
mockConsole p inp = (a, s)
    where
        (a, s, _) = runRWS (interpRWS p) inp []

运行TeletypeRWS“程序”时,环境中的第一个值不会被移除:

*Main> mockConsole hello ["john", "18"]
((),["What is your name?","What is your age?","Hello, john!","You are john years old!"])

我对更新Reader 有点不安,但我不知道我还能如何访问列表中的下一个值。 TeletypeRWS 的类型是根据上面提到的练习选择的——所以我认为应该可以实现 interpRWS

【问题讨论】:

    标签: haskell free-monad


    【解决方案1】:

    我们不能使用foldFree:它需要在延续中是参数的,所以我们不能在那里应用local。相比之下,iterM 明确地为我们提供了没有泛化的实际延续,所以这将起作用。

    interpRWS = iterM lift where
      lift (Put s a) = modify (\t -> t ++ [s]) >> a
      lift (Get f)   = reader head >>= local tail . f
    

    【讨论】:

    • 非常感谢您的回答!建议的更改确实按预期工作,但我在理解为什么这样做时遇到了一些麻烦。 iterMfoldFree 这两个函数似乎非常相关,我不确定我是否掌握其中的差异——我什么时候应该更喜欢其中一个?例如,我还可以根据iterM 实现interpIO。为什么当前基于foldFreeinterpIO 实现可以工作?
    • local 修改运行计算的环境,该环境作为参数提供给它。使用foldFree,您无法访问要应用local 的计算。在foldFree :: ... =&gt; (forall x. f x -&gt; m x) -&gt; Free f a -&gt; m a 中,该计算隐藏在普遍量化的x 中。在iterM :: ... =&gt; (f (m a) -&gt; m a) -&gt; Free f a -&gt; m a 中,计算是m af 中,因此您有机会使用local
    • 没有通用的规则来决定使用哪一个。无论您可以使用foldFree 做什么,您都可以使用iterM,并且在最低级别您可以只对Free 值进行模式匹配。但是,一旦您熟悉了免费的 monad,更高级别的抽象 foldFree 通常会使程序更容易理解。
    猜你喜欢
    • 1970-01-01
    • 2017-01-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-28
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多