2016-05-26 98 views
1

我需要備份一些數據以便以後訪問它。在Haskell中隱藏函數參數?

在接口級別,我有兩個功能:

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

什麼是實現這一目標的最簡單的方法?

+0

你可以用一個MVar來做它,儘管我不能很好地推薦這個。爲什麼不創建備份monad並在其中進行計算? –

+2

您是否熟悉State monad/StateT monad變壓器? – ErikR

+0

我並不熟悉單子,也不熟悉國家,也不熟悉變壓器。在爲我的問題尋找解決方案的同時,我的印象是這是一條正確的路。我試圖實現它,但沒有成功。如果有人能夠在我的代碼中「注入State Monad」,我認爲這將是我能找到的最佳教程。 –

回答

4

以下是使用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與其他monads結合在一起;在這種情況下,允許使用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 

只是要警告,這通常不是推薦的方法。

+0

這完全回答了我的問題。現在,有沒有一種方法可以使它在ghci級別直接使用'save'和'retrieve',例如 * Main> save「a」 * Main> id0:a * Main> save「b」 * Main> id1:b * Main> let result = retrieve「id1」 –

+2

您可以編寫自己的Monad實例,但仍然需要某種類似runState的函數來提供初始狀態。我認爲你所建議的將需要頂級可變參考,這不是慣用的Haskell。 – user2297560

+0

您可能會考慮使用'MonadState s m'和'MonadIO m'上下文,而不是直接使用'StateT s IO'。 – dfeuer