在Haskell中隐藏函数参数?

在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

我需要备份一些数据以便以后访问

在接口级别,我有两个功能:

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