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