【问题标题】:How to hide state from functions that call other functions that use that state如何从调用使用该状态的其他函数的函数中隐藏状态
【发布时间】:2018-10-20 12:28:09
【问题描述】:

我希望我的 Haskell 程序中有一些更高级别的函数调用其他函数,这些函数最终调用使用某些状态或配置的函数,而不必在所有这些函数调用中传递状态。我知道这是 state monad(或者可能是 Reader monad?)的经典用法。

(我也不确定是否应该使用 StateT(如下面的示例)来启用 IO,或者是否应该以某种方式单独输出结果。)

在这个阶段,我对这里的所有教程、博客文章和类似问题感到很困惑,无法找出解决方案。还是我误解了隐藏的东西?

这是一个小例子:

import Control.Monad.State

-- Here's a simple configuration type:
data Config = MkConfig {
      name :: String
    , num  :: Int
    } deriving Show

-- Here's a couple of configurations.
-- (They're hard coded and pre-defined.)
c1 = MkConfig "low" 7
c2 = MkConfig "high" 10

-- Here's a lower level function that explicitly uses the config.
-- (The String is ignored here for simplicity, but it could be used.)
fun :: Config -> Int -> Int
fun (MkConfig _ i) j = i*j

-- testA and GoA work fine as expected.
-- fun uses the different configs c1,c2 in the right way.
testA = do
    a <- get
    lift (print (fun a 2))
    put c2
    a <- get
    lift (print (fun a 4))

goA = evalStateT testA c1
-- (c1 could be put at the start of testA instead.)

-- But what I really want is to use fun2 that calls fun, 
-- and not explicitly need state.
-- But this function definition does not compile:
fun2 :: Int -> Int
fun2 j = 3 * fun cf j  
-- fun needs a config arg cf, but where from?

-- I would like a similar way of using fun2 as in testB and goB here.
testB = do
    a <- get
    lift (print (fun2 3))  -- but fun2 doesn't take the state in a 
    put c2
    a <- get
    lift (print (fun2 42))  -- but fun2 doesn't take the state in a 

goB = evalStateT testB c1 

我想在我的程序中将配置隐藏在诸如 fun2 之类的高级函数之外,同时仍保留更改配置并使用新配置运行这些函数的能力。这是一个“如何做的问题”(除非我的想法完全错误)。

【问题讨论】:

    标签: haskell state-monad


    【解决方案1】:

    当然,你不能在类型签名中完全“隐藏配置”:一个普通的旧函数 Int -&gt; Int 必须是引用透明的,因此它也不能依赖或接受一些 Config 值.

    您可能想要做的是:

    fun2 :: Int -> State Config Int    -- An `Int -> Int` that depends on `Config` state.
                                       -- Compare to how `Int -> IO Int` is like an
                                       -- `Int -> Int` function that depends on IO.
    fun2 j = do
      c1 <- get
      return (3 * fun c1 j)
    

    然后无论您有c :: Config,您都可以通过类似的方式获得结果

    let result = evalState (fun2 42) c    -- An Int.
    

    另见Combining StateT IO with State

    hoistState :: Monad m => State s a -> StateT s m a
    hoistState = StateT . (return .) . runState
    

    然后你可以写类似的东西

    testB :: StateT Config IO ()
    testB = do
        -- Fancy:
        result <- hoistState (fun2 42)
    
        -- Equivalent:
        c <- get
        let result' = evalState (fun2 42) c
    
        lift (print (result, result'))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-14
      相关资源
      最近更新 更多