如何对调用使用该状态的其他函数的函数隐藏状态



我希望在我的Haskell程序中有一些更高级的函数调用其他函数,这些函数最终调用使用某种状态或配置的函数,而不必在所有这些函数调用周围传递状态。我知道这是状态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(之外,同时仍然保留更改配置并使用新配置运行这些功能的能力。这是一个"如何做的问题"(除非我完全错误的想法(。

当然,你不能在类型签名中完全"隐藏配置":一个普通的旧函数Int -> 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.

另请参阅将状态 T IO 与状态相结合:

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'))

最新更新