在Haskell中隐藏函数参数?
我需要备份一些数据以便以后访问 在接口级别,我有两个功能: put:备份数据并返回备份Id 获取:检索给定备份Id的数据 我当前的代码要求我为这两个函数提供backup参数在Haskell中隐藏函数参数?,haskell,state-monad,Haskell,State Monad,我需要备份一些数据以便以后访问 在接口级别,我有两个功能: put:备份数据并返回备份Id 获取:检索给定备份Id的数据 我当前的代码要求我为这两个函数提供backup参数 import Data.Maybe data Data = Data String deriving Show type Backup = [(String,Data)] put :: Backup -> String -> IO Backup put boilerPlate a = do let
import Data.Maybe
data Data = Data String deriving Show
type Backup = [(String,Data)]
put :: Backup -> String -> IO Backup
put boilerPlate a =
do let id = "id" ++ show(length (boilerPlate))
putStrLn $ id ++": " ++ a
return ((id,(Data a)):boilerPlate)
get :: Backup -> String -> Maybe Data
get boilerPlate id = lookup id (boilerPlate)
它工作正常
在以下示例中,备份了两个值。第二个被检索
main :: IO ()
main = do
let bp0 = []
bp1 <- put bp0 "a"
bp2 <- put bp1 "b"
let result = get bp2 "id1"
putStrLn $ "Looking for id1: " ++ show (fromJust(result))
实现这一点最简单的方法是什么?下面是一个使用StateT的示例。请注意,函数名已更改,因为State和StateT已经有get和put函数
module Main where
import Control.Monad.State
data Data = Data String deriving Show
type Backup = [(String,Data)]
save :: String -> StateT Backup IO ()
save a = do
backup <- get
let id = "id" ++ ((show . length) backup)
liftIO $ putStrLn $ id ++ ": " ++ a
put ((id, Data a):backup)
retrieve :: String -> StateT Backup IO (Maybe Data)
retrieve id = do
backup <- get
return $ lookup id backup
run :: IO (Maybe Data)
run = flip evalStateT [] $ do
save "a"
save "b"
retrieve "id1"
main :: IO ()
main = do
result <- run
print result
这还需要{LANGUAGE FlexibleContexts-}这种方法的优点是它允许我们的函数与任何提供备份状态和IO的monad一起工作。特别是,我们可以向monad添加效果,这些函数仍然可以工作
所有这些monad/monad transformer的东西一开始可能会让人很困惑,但一旦你习惯了,它实际上是相当优雅的。优点是您可以很容易地看到每个函数中需要什么样的效果。也就是说,我不想让你认为有些事情是Haskell做不到的,所以这里有另一种方法来实现你的目标,它废除了状态单子,而支持可变引用
module Main where
import Data.IORef
data Data = Data String deriving Show
type Backup = [(String,Data)]
mkSave :: IORef Backup -> String -> IO ()
mkSave r a = do
backup <- readIORef r
let id = "id" ++ ((show . length) backup)
putStrLn $ id ++ ": " ++ a
writeIORef r ((id, Data a):backup)
mkRetrieve :: IORef Backup -> String -> IO (Maybe Data)
mkRetrieve r id = do
backup <- readIORef r
return $ lookup id backup
main :: IO ()
main = do
ref <- newIORef []
let save = mkSave ref
retrieve = mkRetrieve ref
save "a"
save "b"
result <- retrieve "id0"
print result
请注意,这通常不是推荐的方法。您可以通过MVar来完成,尽管我不能凭良心推荐。为什么不创建一个备份monad并在其中进行计算呢?您熟悉State monad/StateT monad transformer吗?我还不熟悉monad、State和transformer。在寻找解决我问题的方法时,我的印象是,这可能是一条正确的道路。我曾试图实现它,但没有成功。如果有人能在我的代码中注入状态单子,我想这将是我能找到的最好的教程。这完美地回答了我的问题。现在,有没有一种方法可以让它在ghci级别直接使用save和retrieve,比如*Main>save a*Main>id0:a*Main>save b*Main>id1:b*Main>let result=retrieve id1您可以编写自己的Monad实例,但仍然需要某种类似于运行状态的函数来提供初始状态。我认为你的建议需要一个顶级的可变引用,这不是惯用的Haskell。你可以考虑使用MaNADStE的M和MadiaDo M上下文,而不是直接使用Stiste的IO。非常感谢@ USER 225760!我已经决定永远学习单子。你的两个例子和你的解释将有助于我最终掌握这个概念。
save :: (MonadState Backup m, MonadIO m) => String -> m ()
retrieve :: (MonadState Backup m, MonadIO m) => String -> m (Maybe Data)
module Main where
import Data.IORef
data Data = Data String deriving Show
type Backup = [(String,Data)]
mkSave :: IORef Backup -> String -> IO ()
mkSave r a = do
backup <- readIORef r
let id = "id" ++ ((show . length) backup)
putStrLn $ id ++ ": " ++ a
writeIORef r ((id, Data a):backup)
mkRetrieve :: IORef Backup -> String -> IO (Maybe Data)
mkRetrieve r id = do
backup <- readIORef r
return $ lookup id backup
main :: IO ()
main = do
ref <- newIORef []
let save = mkSave ref
retrieve = mkRetrieve ref
save "a"
save "b"
result <- retrieve "id0"
print result