1

私は Haskell を初めて使用するので、型インスタンス等式制約など、Yesod で使用されるすべての高度な機能を吸収するのに苦労しています。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

私は何を間違っていますか?

4

1 に答える 1

3

質問を読み直した後、簡単な答えは、持ち上げられたブラケットを使用することだと思います。これにより、すべてのトランスの問題が処理されます。何が起こっているのかについてもう少し洞察を与えるかもしれないので、元の答えも残します。


この場合の問題は、 の使い方ですliftIO。型シグネチャを見てみましょう。

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

これが意味することは、任意の I/O アクション (例えば、ファイルからの読み取り) を取り、それを I/O を実行できる任意のモナド (データベースモナドなど) で実行できるということです。しかし、あなたがやろうとしていることは実際には逆です: データベースモナドアクションを通常の I/O アクションとして実行します。IOデータベースのアクションは、モナドが提供しない特別なコンテキスト、特にデータベース接続に依存しているため、これを直接行うことはできません。

では、データベース アクションを IO アクションに変換するにはどうすればよいでしょうか。アンラップする必要があります。アンラップはモナド トランスフォーマーの一般的なアクティビティであり、互いの上に追加されたレイヤーと考えることができます。リフティング ( のようにliftIO) を使用すると、下のレイヤーからアクションを実行し、それを上のレイヤーに移動できます。ラップを解除すると、レイヤーが取り除かれます。アンラップ方法は特定のトランスフォーマーによって異なるため、各トランスフォーマーには独自のアンラップ機能があります。

Persistent の場合、バックエンドに withSqliteConn または同等のものを使用できます。詳細については、Persistent の章の概要を参照してください。

于 2013-01-18T08:53:40.150 に答える