2013-01-18 131 views
1

我是Haskell的新手,因此我無法吸收Yesod中使用的所有高級功能,例如type instancesequality constraints。我試圖在Yesod的測試框架中實現bracket pattern以獲得setUp/tearDown功能。這裏就是我這麼遠(通過編輯更新):Yesod /持久測試支架模式

module FishMother where 

import Control.Exception.Lifted 

import TestImport 
import Database.Persist 
import Database.Persist.GenericSql 

import Model 

insertYellowfinTuna :: OneSpec Connection FishId 
insertYellowfinTuna = runDB . insert $ Fish "Yellowfin Tuna" 

deleteFish :: FishId -> OneSpec Connection() 
deleteFish = runDB . delete 

withYellowfinTuna :: FishId -> OneSpec Connection() 
withYellowfinTuna = bracket insertYellowfinTuna deleteFish 

編譯錯誤如下:

tests/FishMother.hs:18:21: 
    Couldn't match type `FishId 
      -> Control.Monad.Trans.State.Lazy.StateT 
        (Yesod.Test.OneSpecData Connection) IO()' 
      with `Key SqlPersist Fish' 
    Expected type: FishId -> OneSpec Connection() 
     Actual type: (FishId 
      -> Control.Monad.Trans.State.Lazy.StateT 
      (Yesod.Test.OneSpecData Connection) IO()) 
      -> Control.Monad.Trans.State.Lazy.StateT 
      (Yesod.Test.OneSpecData Connection) IO() 
    In the return type of a call of `bracket' 
    In the expression: bracket insertYellowfinTuna deleteFish 
    In an equation for `withYellowfinTuna': 
    withYellowfinTuna = bracket insertYellowfinTuna deleteFish 

我在做什麼錯?

+1

暫時忘掉Yesod吧。如果這只是普通的'IO',你可以使用函數'setup :: IO FishId','tearDown :: FishId - > IO()',然後'withYellowfinTuna =括號設置tearDown'。先了解該函數的類型,然後回到Yesod世界,嘗試用'OneSpec Connection'單元替換'IO'。 –

+0

謝謝@ michael-snoyman。有時你只需退後一步,甚至可以睡一覺,以解決問題。通過'OneSpec Connection'和其他功能的類型,這也有助於我瞭解自己在正確的軌道上。這是我正在尋找的更正的類型簽名:'withYellowfinTuna ::(FishId - > OneSpec連接()) - > OneSpec連接()' – arussell84

回答

3

重讀這個問題後,我認爲最簡單的答案是使用a lifted bracket,它將處理所有的變壓器問題。我也會留下我的原始答案,因爲它可以更深入地瞭解發生的事情。


這種情況下的問題是使用liftIO。讓我們來看看類型簽名:

liftIO :: MonadIO m => IO a -> m a 

這意味着,你可以採取任意的I/O操作(例如,從文件中讀取),並在任何單子,讓我/要執行Ø運行,比如數據庫monad。然而,你試圖做的事實恰恰相反:將數據庫monad操作作爲普通的I/O操作。這不能直接完成,因爲數據庫操作依賴於額外的上下文,特別是數據庫連接,而monod不提供此額外上下文。

那麼如何將數據庫操作轉換爲IO操作?我們必須展開。解包是monad變壓器的一項常見活動,它可以被認爲是相互疊加的層。舉升(如liftIO)可讓您從較低層採取動作,並將其移至較高層。展開取消了一層。由於你如何拆封取決於特定的變壓器,因此每個變壓器都有自己的拆封功能。

對於Persistent,您可以使用withSqliteConn或您的後端等價物,請參閱Persistent chapter中的摘要以獲取更多詳細信息。

+0

感謝您的線索。我刪除了'liftIO'調用並正在使用提升的'支架',但我仍然遇到麻煩。我認爲我需要的一個線索是與黃鰭金槍魚的類型簽名。我知道它不應該是'FishId - > PersistMonadBackend()',但我無法弄清它*應該是什麼!我也許應該說,我寧願提到我寧願儘可能避免使用特定於數據庫的調用,例如'withSqliteConn'。不幸的是,'runDB','with sqliteConn'和'runSqlConn'之間的關係對我來說還不是很清楚。 – arussell84

+0

我已經更新了原始帖子,並在閱讀完答案後最終結束了,但我仍然陷於困境。我想,我很難搞清楚這一點,因爲我不知道我的'Fish'模型中的'insert'和'delete'的類型簽名。另外,在這些地方調用'runDB'實際上是否合適?爲什麼?我不知道爲什麼我的返回類型是調用'runDB'後的'OneSpec Connection()',但這正是編譯錯誤告訴我使用的! – arussell84