Любой способ сделать более чистую версию этой функции Hocell IO?

У меня есть большое количество функций IConnection conn = > conn → IO(), которые мне нужно выполнить для правильной настройки базы данных. Теперь, это не очень красиво, как есть, но я слишком много начинаю в Haskell, чтобы сделать его лучше.

setup :: IConnection conn => conn -> IO ()
setup conn = do 
 setupUtterances conn
 commit conn
 setupSegments conn
 commit conn
 setupLevels conn
 commit conn
 setupLevelLevel conn
 commit conn
 setupTCLevelLevel conn
 commit conn
 setupPaths conn
 commit conn
 setupLabelTypes conn
 commit conn 
 setupLegalLabels conn
 commit conn
 setupTracks conn
 commit conn
 setupVariables conn
 commit conn 
 setupFeatures conn
 commit conn
 setupAssociations conn
 commit conn
 return ()

В любом случае, чтобы сократить его? Я играл с

sequence $ map ($ conn) [func1, func2,...]

Но я не могу заставить его работать. Предложения?

3 ответа

Просто соберите список функций, нарисуйте приложение функции и переместите коммиты. Затем вы просто упорядочиваете свои действия и вручную вызываете заключительную фиксацию:

import Data.List (intersperse)
-- ... other things
setup :: IConnection => -> IO ()
setup conn =
 let ops = [ setupUtterances
 , setupSegments
 , setupLevels
 , setupLevelLevel
 , setupTCLevelLevel
 , setupPaths
 , setupLabelTypes
 , setupLegalLabels
 , setupTracks
 , setupVariables
 , setupFeatures
 , setupAssociations
 ]
 acts = map ($ conn) $ intersperse commit ops
 in sequence_ acts >> commit conn


Как насчет просто

setup conn = mapM_ ($ conn) $ intersperse commit
 [ setupUtterances,
 , setupSegments
 , setupLevels
 , setupLevelLevel
 , setupTCLevelLevel
 , setupPaths
 , setupLabelTypes
 , setupLegalLabels
 , setupTracks 
 , setupVariables 
 , setupFeatures 
 , setupAssociations
 , \_ -> return ()]

intersperse придерживаться commit между всеми действиями и mapM_ ($conn) для подачи conn ко всем действиям IO. Окончательный \_ -> return () должен гарантировать, что commit вызывается в конце всего.


Мне кажется, что сегодня я играю в гольф. Однострочный (ну, он начинался как один):

import Control.Monad.Trans.Reader
-- | Run a sequence of actions with the same connection, committing that 
-- connection after each action.
runSetup :: IConnection conn => [conn -> IO ()] -> conn -> IO ()
runSetup = runReaderT . mapM_ withCommit
 where withCommit action = ReaderT action >> ReaderT commit
setup = runSetup actions 
 where actions = [ setupUtterances
 , setupSegments
 , setupLevels
 , setupLevelLevel
 , setupTCLevelLevel
 , setupPaths
 , setupLabelTypes
 , setupLegalLabels
 , setupTracks
 , setupVariables
 , setupFeatures
 , setupAssociations
 ]

Основная идея здесь в том, что Connection -> IO () совпадает с ReaderT Connection IO (): a IO (), который "отсутствует" a Connection. ReaderT позволяет обрабатывать commit, setupUtterances, setupSegments и друзей не как функции, а как действия, которые имеют общий, неявный Connection. ReaderT Connection IO - это просто монада, поэтому вы можете просто напасть на commit после каждого из действий легко.

licensed under cc by-sa 3.0 with attribution.