Я новичок Haskell, и я думаю о том, как я могу модулировать приложение Rest, которое, по существу, проходит вокруг ReaderT везде. Я разработал примитивный рабочий пример того, как это сделать (ниже) с помощью ExistentialQuantification. В комментарии к соответствующему ответу пользователь UserMotoryOrchid утверждал что-то похожее на анти-шаблон. Это анти-шаблон? В новинках вы можете объяснить, почему, если так, и показать лучшую альтернативу?
{-# LANGUAGE ExistentialQuantification #-}
import Control.Monad.Reader
import Control.Monad.Trans
import Data.List (intersect)
data Config = Config Int Bool
data User = Jane | John | Robot deriving (Show)
listUsers = [Jane, John, Robot]
class Database d where
search :: d -> String -> IO [User]
fetch :: d -> Int -> IO (Maybe User)
data LiveDb = LiveDb
instance Database LiveDb where
search d q = return $ filter ((q==) . intersect q . show) listUsers
fetch d i = return $ if i<3 then Just $ listUsers!!i else Nothing
data TestDb = TestDb
instance Database TestDb where
search _ _ = return [Robot]
fetch _ _ = return $ Just Robot
data Context = forall d. (Database d) => Context {
db :: d
, config :: Config
}
liveContext = Context { db = LiveDb, config = Config 123 True }
testContext = Context { db = TestDb, config = Config 123 True }
runApi :: String -> ReaderT Context IO String
runApi query = do
Context { db = db } <- ask
liftIO . fmap show $ search db query
main = do
let q = "Jn"
putStrLn $ "searching users for " ++ q
liveResult <- runReaderT (runApi q) liveContext
putStrLn $ "live result " ++ liveResult
testResult <- runReaderT (runApi q) testContext
putStrLn $ "test result " ++ testResult
Изменить: рабочий пример, основанный на принятом ответе
import Control.Monad.Reader
import Control.Monad.Trans
import Data.List (intersect)
data Config = Config Int Bool
data User = Jane | John | Robot deriving (Show)
listUsers = [Jane, John, Robot]
data Database = Database {
search :: String -> IO [User]
, fetch :: Int -> IO (Maybe User)
}
liveDb :: Database
liveDb = Database search fetch where
search q = return $ filter ((q==) . intersect q . show) listUsers
fetch i = return $ if i<3 then Just $ listUsers!!i else Nothing
testDb :: Database
testDb = Database search fetch where
search _ = return [Robot]
fetch _ = return $ Just Robot
data Context = Context {
db :: Database
, config :: Config
}
liveContext = Context { db = liveDb, config = Config 123 True }
testContext = Context { db = testDb, config = Config 123 True }
runApi :: String -> ReaderT Context IO String
runApi query = do
d <- fmap db $ ask
liftIO . fmap show $ search d $ query
main = do
let q = "Jn"
putStrLn $ "searching users for " ++ q
liveResult <- runReaderT (runApi q) liveContext
putStrLn $ "live result " ++ liveResult
testResult <- runReaderT (runApi q) testContext
putStrLn $ "test result " ++ testResult