【问题标题】:Any way to make a cleaner version of this Haskell IO function?有什么方法可以制作这个 Haskell IO 函数的更简洁版本吗?
【发布时间】:2014-08-05 19:38:52
【问题描述】:

我有大量的 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,...]

但我无法让它工作。有什么建议吗?

【问题讨论】:

    标签: haskell io-monad


    【解决方案1】:

    怎么样

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

    interspersecommit 粘贴在所有操作之间,mapM_ ($conn)conn 提供给所有IO 操作。最后的\_ -> return () 是为了确保在这一切结束时调用commit

    【讨论】:

    • 很好,我喜欢 nop 来获得最终提交。
    • 我认为你的. 应该是$
    • @Mankarse 它是我在尝试编辑时通过手机添加的,对此感到抱歉
    • 我也很想写这个答案,但它不需要所有函数都具有相同的类型吗?如果其中一个返回 IO Int 而其余返回 IO() 怎么办?那么原来的do 会起作用,但这种方法不会,因为不可能创建函数列表。
    • 整洁!谢谢,@jozefg:我没有想到必须存在像 void 这样的函数。
    【解决方案2】:

    只需收集函数列表,映射函数应用程序并散布提交。然后您只需对您的操作进行排序并手动调用最终提交:

    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
    

    【讨论】:

    • 完美!正是我想要的。谢谢!
    • 我认为你有一个语法错误:do.. 是语法错误,但do 也不需要。)
    【解决方案3】:

    我今天想做一些代码高尔夫。单线(嗯,一开始就是这样):

    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 () 相同:一个IO ()“缺少”一个ConnectionReaderT 允许您将commitsetupUtterancessetupSegments 和朋友视为共享一个共同的、隐含的Connection 的操作而不是函数。 ReaderT Connection IO 只是一个 monad,所以您可以在每个动作之后轻松添加 commit

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-14
      • 2011-08-27
      • 2021-12-12
      • 1970-01-01
      • 2019-02-04
      • 1970-01-01
      • 1970-01-01
      • 2013-02-04
      相关资源
      最近更新 更多