在 Haskell 中隐藏函数参数?

Hide a function parameter in Haskell?

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

在接口层面,我有两个功能:

我当前的代码要求我为这两个函数提供备份参数。

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

但我需要通过删除所有备份参数来简化 putget 的签名。

我需要这样的东西:

main = do
    put "a"
    put "b"    
    let result = get "id1"

实现此目标的最简单方法是什么?

这是一个使用 StateT 的示例。请注意,函数名称已更改,因为 StateStateT 已经具有 getput 函数。

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

State monad 通过计算线程化了一个 'mutable' 值。 StateTState 与其他单子组合;在这种情况下,允许使用 IO.

正如 dfeuer 提到的,可以使用这些类型使 saveretrieve 更通用一些:

save :: (MonadState Backup m, MonadIO m) => String -> m ()
retrieve :: (MonadState Backup m, MonadIO m) => String -> m (Maybe Data)

(这也需要{-# LANGUAGE FlexibleContexts #-})这种方法的优点是它允许我们的函数与任何提供备份状态和 IO 的 monad 一起工作。特别是,我们可以向 monad 添加效果,这些功能仍然有效。

所有这些 monad / monad transformer 的东西一开始可能会很混乱,但一旦你习惯了它实际上非常优雅。好处是你可以很容易地看到每个功能需要什么样的效果。话虽这么说,我不希望你认为有些事情 Haskell 不能做,所以这是实现你的目标的另一种方法,它取消了状态 monad,转而支持可变引用。

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

请注意,这通常不是推荐的方法。