没有函数参数用法的共享对象

时间:2014-03-15 11:52:16

标签: haskell

我在do块中有一些函数(其中一些是嵌套的),它们有一个共享对象(例如,一个用户会话):

main = do
    session <- establishConnection
    user <- getUser
    firstFunction user session
    secondFunction session

firstFunction user session = do
    options <- getOptions
    nestedFunction options session

我想避免函数中有许多重复的参数,那么如何在没有函数参数的情况下将session / firstFunction / secondFunction传递给nestedFunction / main = do session <- establishConnection user <- getsUser firstFunction user firstFunction user = do session <- someMagic .... 用法? E.g:

{{1}}

3 个答案:

答案 0 :(得分:1)

根据您的功能是否会改变“共享对象”,您可以使用Reader monad或State monad。第一个用于“共享对象”是只读的,第二个用于读取和修改“共享对象”。我将使用Reader做一个例子,因为它更容易:

import Control.Monad.Reader

data Config = Config {
    user :: String
  , session :: Session
  }

type ConnectionState a = ReaderT Config IO a

firstFunction :: ConnectionState ()
firstFunction = do
    conf <- ask
    -- do something with conf
    return ()

secondFunction :: ConnectionState ()
secondFunction = do
    conf <- ask
    -- do something with conf
    return ()

realMain :: ConnectionState ()
realMain = do
    firstFunction
    secondFunction

main :: IO ()
main = do
    conn <- establishConnection
    user <- getUser
    runReaderT realMain (Config user conn)

Config数据类型是您要传递给函数的信息,可以随意添加或删除字段。 ConnectionStateReader monad,您将用它来“共享”Config。它基本上将Config封装在“只读容器”中。每个函数都有ConnectionState类型,可以使用函数ask从monad中读取Config。请注意,在我的示例中,每个函数都有ConnectionState ()类型但您 main函数用于创建Config,然后使用它来调用realMainrealMain调用程序中涉及的所有需要​​Config的函数。

编辑:请注意,如果您不希望user在共享对象中,但作为firstFunction的参数,则可以将其从Config中移除并更改firstFunction的类型到firstFunction :: String -> ConnectionState ()

答案 1 :(得分:1)

有两种方式。

第一个可能是两个中更容易被接受的,但它也更复杂。您使用状态monad(或者如果您不需要修改Session monad的会话)。由于您在IO中工作,您还需要monad转换以将它们中的两个堆叠在一起。

http://en.wikibooks.org/wiki/Haskell/Monad_transformers

要使用的库名为(mtl)。

您的功能类型将变为:

firstFunction :: User -> ReaderT Session IO returntype

如果你在项目中经常使用它,你可以创建一个类型同义词

type MyMonad a = ReaderT Session IO a

你的someMagic函数会被问到

firstFunction user = do
    session <- ask

这是因为最外层的monad是ReaderT,如果你想执行IO,你必须解除它。使用liftIO功能可以达到最佳效果。但是,您无需执行任何操作来运行同一monad的第三个函数。

firstFunction user = do
    session <- ask
    liftIO (do some io)
    thirdFunction

当然,您需要在首次运行monad时指定会话

main = do
    session <- establishConnection
    flip runReader session $ do
        firstFunction
        secondFunction

第二种方式是黑客攻击

http://www.haskell.org/haskellwiki/Top_level_mutable_state

它让您拥有全球IORef,您可以从任何地方访问。这样做的好处是可以避免所有烦人的升力功能。缺点是它是一个黑客,它是全球性的:)。

答案 2 :(得分:1)

第三个解决方案是使用闭包:在session块中使用let制作本地函数,使用do - 块:

main = do
    session <- establishConnection
    user <- getUser
    let firstFunction = do
        options <- getOptions
        nestedFunction options session
    let secondFunction = do
        <...>
    firstFunction
    secondFunction