在 Haskell 中隐藏函数参数?
Hide a function parameter in Haskell?
我需要备份一些数据以便以后访问。
在接口层面,我有两个功能:
put
:备份数据和returns一个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))
但我需要通过删除所有备份参数来简化 put
和 get
的签名。
我需要这样的东西:
main = do
put "a"
put "b"
let result = get "id1"
实现此目标的最简单方法是什么?
这是一个使用 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
State
monad 通过计算线程化了一个 'mutable' 值。 StateT
将 State
与其他单子组合;在这种情况下,允许使用 IO.
正如 dfeuer 提到的,可以使用这些类型使 save
和 retrieve
更通用一些:
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
请注意,这通常不是推荐的方法。
我需要备份一些数据以便以后访问。
在接口层面,我有两个功能:
put
:备份数据和returns一个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))
但我需要通过删除所有备份参数来简化 put
和 get
的签名。
我需要这样的东西:
main = do
put "a"
put "b"
let result = get "id1"
实现此目标的最简单方法是什么?
这是一个使用 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
State
monad 通过计算线程化了一个 'mutable' 值。 StateT
将 State
与其他单子组合;在这种情况下,允许使用 IO.
正如 dfeuer 提到的,可以使用这些类型使 save
和 retrieve
更通用一些:
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
请注意,这通常不是推荐的方法。