haskell - 在 Haskell 中隐藏函数参数?

标签 haskell state-monad

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

在界面层面,我有两个功能:

  • put:备份数据并返回backup_Id

  • get:检索给定backup_Id的数据。

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

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 通过计算线程化“可变”值。 StateTState 与其他 monad 结合起来;在本例中,允许使用 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 转换器的东西一开始可能会非常令人困惑,但一旦你习惯了它,它实际上非常优雅。优点是可以很容易地看到每个功能需要什么样的效果。话虽这么说,我不希望你认为有些事情 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

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

关于haskell - 在 Haskell 中隐藏函数参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37455944/

相关文章:

haskell - Haskell 中的阻塞队列实现

haskell - 为什么 ParsecT 类型有 'u' 参数?

haskell - Haskell 中不同的、交互的状态级别

haskell - get 和 put 函数的作用是什么?

haskell - 如何使用简单的技术用我自己的底层表示(类似于 STRef 或 STArray)在 ST-monad 中实现操作?

haskell - 安装 scion 浏览器时出错

haskell - 我可以命名函数签名吗?

haskell - 在 Haskell 中使用表情符号

haskell - 关于应用和嵌套 Maybe 的问题

arrays - 无法为使用 STUArray 的函数找到合适的签名(GHC 也不能)